Merge pull request #2 from mailchuck/master

Merge Mailchuck
This commit is contained in:
Mendaxi 2015-12-09 09:49:44 -06:00
commit 4f461c61b0
59 changed files with 7882 additions and 2647 deletions

3
.gitignore vendored
View File

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

View File

@ -19,6 +19,14 @@ pseudo-mailing list:
BM-2D9QKN4teYRvoq2fyzpiftPh9WP9qggtzh BM-2D9QKN4teYRvoq2fyzpiftPh9WP9qggtzh
This fork
---------
The purpose of this fork is to add features for server deployment for a
bitmessage/email gateway. It contains merged support for OpenCL PoW and a
couple of new/modified API calls.
References References
---------- ----------
* [Project Website](https://bitmessage.org) * [Project Website](https://bitmessage.org)

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." echo "Creating OS X packages for Bitmessage."
export PYBITMESSAGEVERSION=$1
cd src && python2.7 build_osx.py py2app cd src && python2.7 build_osx.py py2app
if [[ $? = "0" ]]; then if [[ $? = "0" ]]; then

View File

@ -55,7 +55,7 @@ def decodeBase58(string, alphabet=ALPHABET):
def encodeVarint(integer): def encodeVarint(integer):
if integer < 0: if integer < 0:
print 'varint cannot be < 0' logger.error('varint cannot be < 0')
raise SystemExit raise SystemExit
if integer < 253: if integer < 253:
return pack('>B',integer) return pack('>B',integer)
@ -66,7 +66,7 @@ def encodeVarint(integer):
if integer >= 4294967296 and integer < 18446744073709551616: if integer >= 4294967296 and integer < 18446744073709551616:
return pack('>B',255) + pack('>Q',integer) return pack('>B',255) + pack('>Q',integer)
if integer >= 18446744073709551616: if integer >= 18446744073709551616:
print 'varint cannot be >= 18446744073709551616' logger.error('varint cannot be >= 18446744073709551616')
raise SystemExit raise SystemExit
class varintDecodeError(Exception): class varintDecodeError(Exception):
@ -185,25 +185,25 @@ def decodeAddress(address):
try: try:
addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9]) addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9])
except varintDecodeError as e: except varintDecodeError as e:
print e logger.error(str(e))
status = 'varintmalformed' status = 'varintmalformed'
return status,0,0,"" return status,0,0,""
#print 'addressVersionNumber', addressVersionNumber #print 'addressVersionNumber', addressVersionNumber
#print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber #print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber
if addressVersionNumber > 4: if addressVersionNumber > 4:
print 'cannot decode address version numbers this high' logger.error('cannot decode address version numbers this high')
status = 'versiontoohigh' status = 'versiontoohigh'
return status,0,0,"" return status,0,0,""
elif addressVersionNumber == 0: elif addressVersionNumber == 0:
print 'cannot decode address version numbers of zero.' logger.error('cannot decode address version numbers of zero.')
status = 'versiontoohigh' status = 'versiontoohigh'
return status,0,0,"" return status,0,0,""
try: try:
streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:]) streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:])
except varintDecodeError as e: except varintDecodeError as e:
print e logger.error(str(e))
status = 'varintmalformed' status = 'varintmalformed'
return status,0,0,"" return status,0,0,""
#print streamNumber #print streamNumber

View File

@ -12,7 +12,7 @@ if __name__ == "__main__":
import sys import sys
sys.exit(0) sys.exit(0)
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
import json import json
import shared import shared
@ -26,7 +26,7 @@ from pyelliptic.openssl import OpenSSL
from struct import pack from struct import pack
# Classes # Classes
from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute,sqlStoredProcedure
from debug import logger from debug import logger
# Helper Functions # Helper Functions
@ -43,6 +43,13 @@ class APIError(Exception):
def __str__(self): def __str__(self):
return "API Error %04i: %s" % (self.error_number, self.error_message) 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 is one of several classes that constitute the API
# This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros). # 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/ # http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/
@ -174,7 +181,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data return data
def HandleListAddressBookEntries(self, params): 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":[' data = '{"addresses":['
for row in queryreturn: for row in queryreturn:
label, address = row label, address = row
@ -956,6 +970,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
message, = params message, = params
shared.UISignalQueue.put(('updateStatusBar', message)) shared.UISignalQueue.put(('updateStatusBar', message))
def HandleDeleteAndVacuum(self, params):
sqlStoredProcedure('deleteandvacuume')
return 'done'
handlers = {} handlers = {}
handlers['helloWorld'] = HandleHelloWorld handlers['helloWorld'] = HandleHelloWorld
@ -1006,6 +1023,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
handlers['getMessageDataByDestinationTag'] = HandleGetMessageDataByDestinationHash handlers['getMessageDataByDestinationTag'] = HandleGetMessageDataByDestinationHash
handlers['clientStatus'] = HandleClientStatus handlers['clientStatus'] = HandleClientStatus
handlers['decodeAddress'] = HandleDecodeAddress handlers['decodeAddress'] = HandleDecodeAddress
handlers['deleteAndVacuum'] = HandleDeleteAndVacuum
def _handle_request(self, method, params): def _handle_request(self, method, params):
if (self.handlers.has_key(method)): 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.ljust(7) + '|'
print ' --------------------------------------------------------------------------\n'
def genAdd(lbl,deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe): #Generate address
global usrPrompt
if deterministic == False: #Generates a new address with the user defined label. non-deterministic
addressLabel = lbl.encode('base64')
try:
generatedAddress = api.createRandomAddress(addressLabel)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return generatedAddress
elif deterministic == True: #Generates a new deterministic address with the user inputs.
passphrase = passphrase.encode('base64')
try:
generatedAddress = api.createDeterministicAddresses(passphrase, numOfAdd, addVNum, streamNum, ripe)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return generatedAddress
else:
return 'Entry Error'
def delMilAddr(): #Generate address
global usrPrompt
try:
response = api.listAddresses2()
# if api is too old just return then fail
if "API Error 0020" in response: return
addresses = json.loads(response)
for entry in addresses['addresses']:
if entry['label'].decode('base64')[:6] == "random":
api.deleteAddress(entry['address'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def genMilAddr(): #Generate address
global usrPrompt
maxn = 0
try:
response = api.listAddresses2()
if "API Error 0020" in response: return
addresses = json.loads(response)
for entry in addresses['addresses']:
if entry['label'].decode('base64')[:6] == "random":
newn = int(entry['label'].decode('base64')[6:])
if maxn < newn:
maxn = newn
except:
print "\n Some error\n"
print "\n Starting at " + str(maxn) + "\n"
for i in range(maxn, 10000):
lbl = "random" + str(i)
addressLabel = lbl.encode('base64')
try:
generatedAddress = api.createRandomAddress(addressLabel)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def saveFile(fileName, fileData): #Allows attachments and messages/broadcats to be saved
#This section finds all invalid characters and replaces them with ~
fileName = fileName.replace(" ", "")
fileName = fileName.replace("/", "~")
#fileName = fileName.replace("\\", "~") How do I get this to work...?
fileName = fileName.replace(":", "~")
fileName = fileName.replace("*", "~")
fileName = fileName.replace("?", "~")
fileName = fileName.replace('"', "~")
fileName = fileName.replace("<", "~")
fileName = fileName.replace(">", "~")
fileName = fileName.replace("|", "~")
directory = 'attachments'
if not os.path.exists(directory):
os.makedirs(directory)
filePath = directory +'/'+ fileName
'''try: #Checks if file already exists
with open(filePath):
print 'File Already Exists'
return
except IOError: pass'''
f = open(filePath, 'wb+') #Begin saving to file
f.write(fileData.decode("base64"))
f.close
print '\n Successfully saved '+ filePath + '\n'
def attachment(): #Allows users to attach a file to their message or broadcast
theAttachmentS = ''
while True:
isImage = False
theAttachment = ''
while True:#loops until valid path is entered
filePath = userInput('\nPlease enter the path to the attachment or just the attachment name if in this folder.')
try:
with open(filePath): break
except IOError:
print '\n %s was not found on your filesystem or can not be opened.\n' % filePath
pass
#print filesize, and encoding estimate with confirmation if file is over X size (1mb?)
invSize = os.path.getsize(filePath)
invSize = (invSize / 1024) #Converts to kilobytes
round(invSize,2) #Rounds to two decimal places
if (invSize > 500.0):#If over 500KB
print '\n WARNING:The file that you are trying to attach is ', invSize, 'KB and will take considerable time to send.\n'
uInput = userInput('Are you sure you still want to attach it, (Y)es or (N)o?').lower()
if uInput != "y":
print '\n Attachment discarded.\n'
return ''
elif (invSize > 184320.0): #If larger than 180MB, discard.
print '\n Attachment too big, maximum allowed size:180MB\n'
main()
pathLen = len(str(ntpath.basename(filePath))) #Gets the length of the filepath excluding the filename
fileName = filePath[(len(str(filePath)) - pathLen):] #reads the filename
filetype = imghdr.what(filePath) #Tests if it is an image file
if filetype is not None:
print '\n ---------------------------------------------------'
print ' Attachment detected as an Image.'
print ' <img> tags will automatically be included,'
print ' allowing the recipient to view the image'
print ' using the "View HTML code..." option in Bitmessage.'
print ' ---------------------------------------------------\n'
isImage = True
time.sleep(2)
print '\n Encoding Attachment, Please Wait ...\n' #Alert the user that the encoding process may take some time.
with open(filePath, 'rb') as f: #Begin the actual encoding
data = f.read(188743680) #Reads files up to 180MB, the maximum size for Bitmessage.
data = data.encode("base64")
if (isImage == True): #If it is an image, include image tags in the message
theAttachment = """
<!-- Note: Image attachment below. Please use the right click "View HTML code ..." option to view it. -->
<!-- Sent using Bitmessage Daemon. https://github.com/Dokument/PyBitmessage-Daemon -->
Filename:%s
Filesize:%sKB
Encoding:base64
<center>
<div id="image">
<img alt = "%s" src='data:image/%s;base64, %s' />
</div>
</center>""" % (fileName,invSize,fileName,filetype,data)
else: #Else it is not an image so do not include the embedded image code.
theAttachment = """
<!-- Note: File attachment below. Please use a base64 decoder, or Daemon, to save it. -->
<!-- Sent using Bitmessage Daemon. https://github.com/Dokument/PyBitmessage-Daemon -->
Filename:%s
Filesize:%sKB
Encoding:base64
<attachment alt = "%s" src='data:file/%s;base64, %s' />""" % (fileName,invSize,fileName,fileName,data)
uInput = userInput('Would you like to add another attachment, (Y)es or (N)o?').lower()
if (uInput == 'y' or uInput == 'yes'):#Allows multiple attachments to be added to one message
theAttachmentS = str(theAttachmentS) + str(theAttachment)+ '\n\n'
elif (uInput == 'n' or uInput == 'no'):
break
theAttachmentS = theAttachmentS + theAttachment
return theAttachmentS
def sendMsg(toAddress, fromAddress, subject, message): #With no arguments sent, sendMsg fills in the blanks. subject and message must be encoded before they are passed.
global usrPrompt
if (validAddress(toAddress)== False):
while True:
toAddress = userInput("What is the To Address?")
if (toAddress == "c"):
usrPrompt = 1
print ' '
main()
elif (validAddress(toAddress)== False):
print '\n Invalid Address. "c" to cancel. Please try again.\n'
else:
break
if (validAddress(fromAddress)== False):
try:
jsonAddresses = json.loads(api.listAddresses())
numAddresses = len(jsonAddresses['addresses']) #Number of addresses
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
if (numAddresses > 1): #Ask what address to send from if multiple addresses
found = False
while True:
print ' '
fromAddress = userInput("Enter an Address or Address Label to send from.")
if fromAddress == "exit":
usrPrompt = 1
main()
for addNum in range (0, numAddresses): #processes all of the addresses
label = jsonAddresses['addresses'][addNum]['label']
address = jsonAddresses['addresses'][addNum]['address']
#stream = jsonAddresses['addresses'][addNum]['stream']
#enabled = jsonAddresses['addresses'][addNum]['enabled']
if (fromAddress == label): #address entered was a label and is found
fromAddress = address
found = True
break
if (found == False):
if(validAddress(fromAddress)== False):
print '\n Invalid Address. Please try again.\n'
else:
for addNum in range (0, numAddresses): #processes all of the addresses
#label = jsonAddresses['addresses'][addNum]['label']
address = jsonAddresses['addresses'][addNum]['address']
#stream = jsonAddresses['addresses'][addNum]['stream']
#enabled = jsonAddresses['addresses'][addNum]['enabled']
if (fromAddress == address): #address entered was a found in our addressbook.
found = True
break
if (found == False):
print '\n The address entered is not one of yours. Please try again.\n'
if (found == True):
break #Address was found
else: #Only one address in address book
print '\n Using the only address in the addressbook to send from.\n'
fromAddress = jsonAddresses['addresses'][0]['address']
if (subject == ''):
subject = userInput("Enter your Subject.")
subject = subject.encode('base64')
if (message == ''):
message = userInput("Enter your Message.")
uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower()
if uInput == "y":
message = message + '\n\n' + attachment()
message = message.encode('base64')
try:
ackData = api.sendMessage(toAddress, fromAddress, subject, message)
print '\n Message Status:', api.getStatus(ackData), '\n'
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def sendBrd(fromAddress, subject, message): #sends a broadcast
global usrPrompt
if (fromAddress == ''):
try:
jsonAddresses = json.loads(api.listAddresses())
numAddresses = len(jsonAddresses['addresses']) #Number of addresses
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
if (numAddresses > 1): #Ask what address to send from if multiple addresses
found = False
while True:
fromAddress = userInput("\nEnter an Address or Address Label to send from.")
if fromAddress == "exit":
usrPrompt = 1
main()
for addNum in range (0, numAddresses): #processes all of the addresses
label = jsonAddresses['addresses'][addNum]['label']
address = jsonAddresses['addresses'][addNum]['address']
#stream = jsonAddresses['addresses'][addNum]['stream']
#enabled = jsonAddresses['addresses'][addNum]['enabled']
if (fromAddress == label): #address entered was a label and is found
fromAddress = address
found = True
break
if (found == False):
if(validAddress(fromAddress)== False):
print '\n Invalid Address. Please try again.\n'
else:
for addNum in range (0, numAddresses): #processes all of the addresses
#label = jsonAddresses['addresses'][addNum]['label']
address = jsonAddresses['addresses'][addNum]['address']
#stream = jsonAddresses['addresses'][addNum]['stream']
#enabled = jsonAddresses['addresses'][addNum]['enabled']
if (fromAddress == address): #address entered was a found in our addressbook.
found = True
break
if (found == False):
print '\n The address entered is not one of yours. Please try again.\n'
if (found == True):
break #Address was found
else: #Only one address in address book
print '\n Using the only address in the addressbook to send from.\n'
fromAddress = jsonAddresses['addresses'][0]['address']
if (subject == ''):
subject = userInput("Enter your Subject.")
subject = subject.encode('base64')
if (message == ''):
message = userInput("Enter your Message.")
uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower()
if uInput == "y":
message = message + '\n\n' + attachment()
message = message.encode('base64')
try:
ackData = api.sendBroadcast(fromAddress, subject, message)
print '\n Message Status:', api.getStatus(ackData), '\n'
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def inbox(unreadOnly = False): #Lists the messages by: Message Number, To Address Label, From Address Label, Subject, Received Time)
global usrPrompt
try:
inboxMessages = json.loads(api.getAllInboxMessages())
numMessages = len(inboxMessages['inboxMessages'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
messagesPrinted = 0
messagesUnread = 0
for msgNum in range (0, numMessages): #processes all of the messages in the inbox
message = inboxMessages['inboxMessages'][msgNum]
# if we are displaying all messages or if this message is unread then display it
if not unreadOnly or not message['read']:
print ' -----------------------------------\n'
print ' Message Number:',msgNum #Message Number
print ' To:', getLabelForAddress(message['toAddress']) #Get the to address
print ' From:', getLabelForAddress(message['fromAddress']) #Get the from address
print ' Subject:', message['subject'].decode('base64') #Get the subject
print ' Received:', datetime.datetime.fromtimestamp(float(message['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S')
messagesPrinted += 1
if not message['read']: messagesUnread += 1
if (messagesPrinted%20 == 0 and messagesPrinted != 0):
uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower()
print '\n -----------------------------------'
print ' There are %d unread messages of %d messages in the inbox.' % (messagesUnread, numMessages)
print ' -----------------------------------\n'
def outbox():
global usrPrompt
try:
outboxMessages = json.loads(api.getAllSentMessages())
numMessages = len(outboxMessages['sentMessages'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
for msgNum in range (0, numMessages): #processes all of the messages in the outbox
print '\n -----------------------------------\n'
print ' Message Number:',msgNum #Message Number
#print ' Message ID:', outboxMessages['sentMessages'][msgNum]['msgid']
print ' To:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['toAddress']) #Get the to address
print ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress']) #Get the from address
print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') #Get the subject
print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] #Get the subject
print ' Last Action Time:', datetime.datetime.fromtimestamp(float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S')
if (msgNum%20 == 0 and msgNum != 0):
uInput = userInput('(Press Enter to continue or type (Exit) to return to the main menu.)').lower()
print '\n -----------------------------------'
print ' There are ',numMessages,' messages in the outbox.'
print ' -----------------------------------\n'
def readSentMsg(msgNum): #Opens a sent message for reading
global usrPrompt
try:
outboxMessages = json.loads(api.getAllSentMessages())
numMessages = len(outboxMessages['sentMessages'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print ' '
if (msgNum >= numMessages):
print '\n Invalid Message Number.\n'
main()
#Begin attachment detection
message = outboxMessages['sentMessages'][msgNum]['message'].decode('base64')
while True: #Allows multiple messages to be downloaded/saved
if (';base64,' in message): #Found this text in the message, there is probably an attachment.
attPos= message.index(";base64,") #Finds the attachment position
attEndPos = message.index("' />") #Finds the end of the attachment
#attLen = attEndPos - attPos #Finds the length of the message
if ('alt = "' in message): #We can get the filename too
fnPos = message.index('alt = "') #Finds position of the filename
fnEndPos = message.index('" src=') #Finds the end position
#fnLen = fnEndPos - fnPos #Finds the length of the filename
fileName = message[fnPos+7:fnEndPos]
else:
fnPos = attPos
fileName = 'Attachment'
uInput = userInput('\n Attachment Detected. Would you like to save the attachment, (Y)es or (N)o?').lower()
if (uInput == "y" or uInput == 'yes'):
attachment = message[attPos+9:attEndPos]
saveFile(fileName,attachment)
message = message[:fnPos] + '~<Attachment data removed for easier viewing>~' + message[(attEndPos+4):]
else:
break
#End attachment Detection
print '\n To:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['toAddress']) #Get the to address
print ' From:', getLabelForAddress(outboxMessages['sentMessages'][msgNum]['fromAddress']) #Get the from address
print ' Subject:', outboxMessages['sentMessages'][msgNum]['subject'].decode('base64') #Get the subject
print ' Status:', outboxMessages['sentMessages'][msgNum]['status'] #Get the subject
print ' Last Action Time:', datetime.datetime.fromtimestamp(float(outboxMessages['sentMessages'][msgNum]['lastActionTime'])).strftime('%Y-%m-%d %H:%M:%S')
print ' Message:\n'
print message #inboxMessages['inboxMessages'][msgNum]['message'].decode('base64')
print ' '
def readMsg(msgNum): #Opens a message for reading
global usrPrompt
try:
inboxMessages = json.loads(api.getAllInboxMessages())
numMessages = len(inboxMessages['inboxMessages'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
if (msgNum >= numMessages):
print '\n Invalid Message Number.\n'
main()
#Begin attachment detection
message = inboxMessages['inboxMessages'][msgNum]['message'].decode('base64')
while True: #Allows multiple messages to be downloaded/saved
if (';base64,' in message): #Found this text in the message, there is probably an attachment.
attPos= message.index(";base64,") #Finds the attachment position
attEndPos = message.index("' />") #Finds the end of the attachment
#attLen = attEndPos - attPos #Finds the length of the message
if ('alt = "' in message): #We can get the filename too
fnPos = message.index('alt = "') #Finds position of the filename
fnEndPos = message.index('" src=') #Finds the end position
#fnLen = fnEndPos - fnPos #Finds the length of the filename
fileName = message[fnPos+7:fnEndPos]
else:
fnPos = attPos
fileName = 'Attachment'
uInput = userInput('\n Attachment Detected. Would you like to save the attachment, (Y)es or (N)o?').lower()
if (uInput == "y" or uInput == 'yes'):
attachment = message[attPos+9:attEndPos]
saveFile(fileName,attachment)
message = message[:fnPos] + '~<Attachment data removed for easier viewing>~' + message[(attEndPos+4):]
else:
break
#End attachment Detection
print '\n To:', getLabelForAddress(inboxMessages['inboxMessages'][msgNum]['toAddress']) #Get the to address
print ' From:', getLabelForAddress(inboxMessages['inboxMessages'][msgNum]['fromAddress']) #Get the from address
print ' Subject:', inboxMessages['inboxMessages'][msgNum]['subject'].decode('base64') #Get the subject
print ' Received:',datetime.datetime.fromtimestamp(float(inboxMessages['inboxMessages'][msgNum]['receivedTime'])).strftime('%Y-%m-%d %H:%M:%S')
print ' Message:\n'
print message #inboxMessages['inboxMessages'][msgNum]['message'].decode('base64')
print ' '
return inboxMessages['inboxMessages'][msgNum]['msgid']
def replyMsg(msgNum,forwardORreply): #Allows you to reply to the message you are currently on. Saves typing in the addresses and subject.
global usrPrompt
forwardORreply = forwardORreply.lower() #makes it lowercase
try:
inboxMessages = json.loads(api.getAllInboxMessages())
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
fromAdd = inboxMessages['inboxMessages'][msgNum]['toAddress']#Address it was sent To, now the From address
message = inboxMessages['inboxMessages'][msgNum]['message'].decode('base64') #Message that you are replying too.
subject = inboxMessages['inboxMessages'][msgNum]['subject']
subject = subject.decode('base64')
if (forwardORreply == 'reply'):
toAdd = inboxMessages['inboxMessages'][msgNum]['fromAddress'] #Address it was From, now the To address
subject = "Re: " + subject
elif (forwardORreply == 'forward'):
subject = "Fwd: " + subject
while True:
toAdd = userInput("What is the To Address?")
if (toAdd == "c"):
usrPrompt = 1
print ' '
main()
elif (validAddress(toAdd)== False):
print '\n Invalid Address. "c" to cancel. Please try again.\n'
else:
break
else:
print '\n Invalid Selection. Reply or Forward only'
usrPrompt = 0
main()
subject = subject.encode('base64')
newMessage = userInput("Enter your Message.")
uInput = userInput('Would you like to add an attachment, (Y)es or (N)o?').lower()
if uInput == "y":
newMessage = newMessage + '\n\n' + attachment()
newMessage = newMessage + '\n\n------------------------------------------------------\n'
newMessage = newMessage + message
newMessage = newMessage.encode('base64')
sendMsg(toAdd, fromAdd, subject, newMessage)
main()
def delMsg(msgNum): #Deletes a specified message from the inbox
global usrPrompt
try:
inboxMessages = json.loads(api.getAllInboxMessages())
msgId = inboxMessages['inboxMessages'][int(msgNum)]['msgid'] #gets the message ID via the message index number
msgAck = api.trashMessage(msgId)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return msgAck
def delSentMsg(msgNum): #Deletes a specified message from the outbox
global usrPrompt
try:
outboxMessages = json.loads(api.getAllSentMessages())
msgId = outboxMessages['sentMessages'][int(msgNum)]['msgid'] #gets the message ID via the message index number
msgAck = api.trashSentMessage(msgId)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
return msgAck
def getLabelForAddress(address):
global usrPrompt
if address in knownAddresses:
return knownAddresses[address]
else:
buildKnownAddresses()
if address in knownAddresses:
return knownAddresses[address]
return address
def buildKnownAddresses():
# add from address book
try:
response = api.listAddressBookEntries()
# if api is too old then fail
if "API Error 0020" in response: return
addressBook = json.loads(response)
for entry in addressBook['addresses']:
if entry['address'] not in knownAddresses:
knownAddresses[entry['address']] = "%s (%s)" % (entry['label'].decode('base64'), entry['address'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
# add from my addresses
try:
response = api.listAddresses2()
# if api is too old just return then fail
if "API Error 0020" in response: return
addresses = json.loads(response)
for entry in addresses['addresses']:
if entry['address'] not in knownAddresses:
knownAddresses[entry['address']] = "%s (%s)" % (entry['label'].decode('base64'), entry['address'])
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def listAddressBookEntries():
try:
response = api.listAddressBookEntries()
if "API Error" in response:
return getAPIErrorCode(response)
addressBook = json.loads(response)
print
print ' --------------------------------------------------------------'
print ' | Label | Address |'
print ' |--------------------|---------------------------------------|'
for entry in addressBook['addresses']:
label = entry['label'].decode('base64')
address = entry['address']
if (len(label) > 19): label = label[:16] + '...'
print ' | ' + label.ljust(19) + '| ' + address.ljust(37) + ' |'
print ' --------------------------------------------------------------'
print
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def addAddressToAddressBook(address, label):
try:
response = api.addAddressBookEntry(address, label.encode('base64'))
if "API Error" in response:
return getAPIErrorCode(response)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def deleteAddressFromAddressBook(address):
try:
response = api.deleteAddressBookEntry(address)
if "API Error" in response:
return getAPIErrorCode(response)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def getAPIErrorCode(response):
if "API Error" in response:
# if we got an API error return the number by getting the number
# after the second space and removing the trailing colon
return int(response.split()[2][:-1])
def markMessageRead(messageID):
try:
response = api.getInboxMessageByID(messageID, True)
if "API Error" in response:
return getAPIErrorCode(response)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def markMessageUnread(messageID):
try:
response = api.getInboxMessageByID(messageID, False)
if "API Error" in response:
return getAPIErrorCode(response)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def markAllMessagesRead():
try:
inboxMessages = json.loads(api.getAllInboxMessages())['inboxMessages']
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
for message in inboxMessages:
if not message['read']:
markMessageRead(message['msgid'])
def markAllMessagesUnread():
try:
inboxMessages = json.loads(api.getAllInboxMessages())['inboxMessages']
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
for message in inboxMessages:
if message['read']:
markMessageUnread(message['msgid'])
def clientStatus():
try:
clientStatus = json.loads(api.clientStatus())
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
print "\nnetworkStatus: " + clientStatus['networkStatus'] + "\n"
print "\nnetworkConnections: " + str(clientStatus['networkConnections']) + "\n"
print "\nnumberOfPubkeysProcessed: " + str(clientStatus['numberOfPubkeysProcessed']) + "\n"
print "\nnumberOfMessagesProcessed: " + str(clientStatus['numberOfMessagesProcessed']) + "\n"
print "\nnumberOfBroadcastsProcessed: " + str(clientStatus['numberOfBroadcastsProcessed']) + "\n"
def UI(usrInput): #Main user menu
global usrPrompt
if usrInput == "help" or usrInput == "h" or usrInput == "?":
print ' '
print ' -------------------------------------------------------------------------'
print ' | https://github.com/Dokument/PyBitmessage-Daemon |'
print ' |-----------------------------------------------------------------------|'
print ' | Command | Description |'
print ' |------------------------|----------------------------------------------|'
print ' | help | This help file. |'
print ' | apiTest | Tests the API |'
print ' | addInfo | Returns address information (If valid) |'
print ' | bmSettings | BitMessage settings |'
print ' | exit | Use anytime to return to main menu |'
print ' | quit | Quits the program |'
print ' |------------------------|----------------------------------------------|'
print ' | listAddresses | Lists all of the users addresses |'
print ' | generateAddress | Generates a new address |'
print ' | getAddress | Get determinist address from passphrase |'
print ' |------------------------|----------------------------------------------|'
print ' | listAddressBookEntries | Lists entries from the Address Book |'
print ' | addAddressBookEntry | Add address to the Address Book |'
print ' | deleteAddressBookEntry | Deletes address from the Address Book |'
print ' |------------------------|----------------------------------------------|'
print ' | subscribe | Subscribes to an address |'
print ' | unsubscribe | Unsubscribes from an address |'
#print ' | listSubscriptions | Lists all of the subscriptions. |'
print ' |------------------------|----------------------------------------------|'
print ' | create | Creates a channel |'
print ' | join | Joins a channel |'
print ' | leave | Leaves a channel |'
print ' |------------------------|----------------------------------------------|'
print ' | inbox | Lists the message information for the inbox |'
print ' | outbox | Lists the message information for the outbox |'
print ' | send | Send a new message or broadcast |'
print ' | unread | Lists all unread inbox messages |'
print ' | read | Reads a message from the inbox or outbox |'
print ' | save | Saves message to text file |'
print ' | delete | Deletes a message or all messages |'
print ' -------------------------------------------------------------------------'
print ' '
main()
elif usrInput == "apitest": #tests the API Connection.
if (apiTest() == True):
print '\n API connection test has: PASSED\n'
else:
print '\n API connection test has: FAILED\n'
main()
elif usrInput == "addinfo":
tmp_address = userInput('\nEnter the Bitmessage Address.')
address_information = api.decodeAddress(tmp_address)
address_information = eval(address_information)
print '\n------------------------------'
if 'success' in str(address_information.get('status')).lower():
print ' Valid Address'
print ' Address Version: %s' % str(address_information.get('addressVersion'))
print ' Stream Number: %s' % str(address_information.get('streamNumber'))
else:
print ' Invalid Address !'
print '------------------------------\n'
main()
elif usrInput == "bmsettings": #tests the API Connection.
bmSettings()
print ' '
main()
elif usrInput == "quit": #Quits the application
print '\n Bye\n'
sys.exit()
os.exit()
elif usrInput == "listaddresses": #Lists all of the identities in the addressbook
listAdd()
main()
elif usrInput == "generateaddress": #Generates a new address
uInput = userInput('\nWould you like to create a (D)eterministic or (R)andom address?').lower()
if uInput == "d" or uInput == "determinstic": #Creates a deterministic address
deterministic = True
#lbl = raw_input('Label the new address:') #currently not possible via the api
lbl = ''
passphrase = userInput('Enter the Passphrase.')#.encode('base64')
numOfAdd = int(userInput('How many addresses would you like to generate?'))
#addVNum = int(raw_input('Address version number (default "0"):'))
#streamNum = int(raw_input('Stream number (default "0"):'))
addVNum = 3
streamNum = 1
isRipe = userInput('Shorten the address, (Y)es or (N)o?').lower()
if isRipe == "y":
ripe = True
print genAdd(lbl,deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe)
main()
elif isRipe == "n":
ripe = False
print genAdd(lbl, deterministic, passphrase, numOfAdd, addVNum, streamNum, ripe)
main()
elif isRipe == "exit":
usrPrompt = 1
main()
else:
print '\n Invalid input\n'
main()
elif uInput == "r" or uInput == "random": #Creates a random address with user-defined label
deterministic = False
null = ''
lbl = userInput('Enter the label for the new address.')
print genAdd(lbl,deterministic, null,null, null, null, null)
main()
else:
print '\n Invalid input\n'
main()
elif usrInput == "getaddress": #Gets the address for/from a passphrase
phrase = userInput("Enter the address passphrase.")
print '\n Working...\n'
#vNumber = int(raw_input("Enter the address version number:"))
#sNumber = int(raw_input("Enter the address stream number:"))
address = getAddress(phrase,4,1)#,vNumber,sNumber)
print ('\n Address: ' + address + '\n')
usrPrompt = 1
main()
elif usrInput == "subscribe": #Subsribe to an address
subscribe()
usrPrompt = 1
main()
elif usrInput == "unsubscribe": #Unsubscribe from an address
unsubscribe()
usrPrompt = 1
main()
elif usrInput == "listsubscriptions": #Unsubscribe from an address
listSubscriptions()
usrPrompt = 1
main()
elif usrInput == "create":
createChan()
userPrompt = 1
main()
elif usrInput == "join":
joinChan()
userPrompt = 1
main()
elif usrInput == "leave":
leaveChan()
userPrompt = 1
main()
elif usrInput == "inbox":
print '\n Loading...\n'
inbox()
main()
elif usrInput == "unread":
print '\n Loading...\n'
inbox(True)
main()
elif usrInput == "outbox":
print '\n Loading...\n'
outbox()
main()
elif usrInput == 'send': #Sends a message or broadcast
uInput = userInput('Would you like to send a (M)essage or (B)roadcast?').lower()
if (uInput == 'm' or uInput == 'message'):
null = ''
sendMsg(null,null,null,null)
main()
elif (uInput =='b' or uInput == 'broadcast'):
null = ''
sendBrd(null,null,null)
main()
elif usrInput == "read": #Opens a message from the inbox for viewing.
uInput = userInput("Would you like to read a message from the (I)nbox or (O)utbox?").lower()
if (uInput != 'i' and uInput != 'inbox' and uInput != 'o' and uInput != 'outbox'):
print '\n Invalid Input.\n'
usrPrompt = 1
main()
msgNum = int(userInput("What is the number of the message you wish to open?"))
if (uInput == 'i' or uInput == 'inbox'):
print '\n Loading...\n'
messageID = readMsg(msgNum)
uInput = userInput("\nWould you like to keep this message unread, (Y)es or (N)o?").lower()
if not (uInput == 'y' or uInput == 'yes'):
markMessageRead(messageID)
usrPrompt = 1
uInput = userInput("\nWould you like to (D)elete, (F)orward, (R)eply to, or (Exit) this message?").lower()
if (uInput == 'r' or uInput == 'reply'):
print '\n Loading...\n'
print ' '
replyMsg(msgNum,'reply')
usrPrompt = 1
elif (uInput == 'f' or uInput == 'forward'):
print '\n Loading...\n'
print ' '
replyMsg(msgNum,'forward')
usrPrompt = 1
elif (uInput == "d" or uInput == 'delete'):
uInput = userInput("Are you sure, (Y)es or (N)o?").lower()#Prevent accidental deletion
if uInput == "y":
delMsg(msgNum)
print '\n Message Deleted.\n'
usrPrompt = 1
else:
usrPrompt = 1
else:
print '\n Invalid entry\n'
usrPrompt = 1
elif (uInput == 'o' or uInput == 'outbox'):
readSentMsg(msgNum)
uInput = userInput("Would you like to (D)elete, or (Exit) this message?").lower() #Gives the user the option to delete the message
if (uInput == "d" or uInput == 'delete'):
uInput = userInput('Are you sure, (Y)es or (N)o?').lower() #Prevent accidental deletion
if uInput == "y":
delSentMsg(msgNum)
print '\n Message Deleted.\n'
usrPrompt = 1
else:
usrPrompt = 1
else:
print '\n Invalid Entry\n'
usrPrompt = 1
main()
elif usrInput == "save":
uInput = userInput("Would you like to save a message from the (I)nbox or (O)utbox?").lower()
if (uInput != 'i' and uInput == 'inbox' and uInput != 'o' and uInput == 'outbox'):
print '\n Invalid Input.\n'
usrPrompt = 1
main()
if (uInput == 'i' or uInput == 'inbox'):
inboxMessages = json.loads(api.getAllInboxMessages())
numMessages = len(inboxMessages['inboxMessages'])
while True:
msgNum = int(userInput("What is the number of the message you wish to save?"))
if (msgNum >= numMessages):
print '\n Invalid Message Number.\n'
else:
break
subject = inboxMessages['inboxMessages'][msgNum]['subject'].decode('base64')
message = inboxMessages['inboxMessages'][msgNum]['message']#Don't decode since it is done in the saveFile function
elif (uInput == 'o' or uInput == 'outbox'):
outboxMessages = json.loads(api.getAllSentMessages())
numMessages = len(outboxMessages['sentMessages'])
while True:
msgNum = int(userInput("What is the number of the message you wish to save?"))
if (msgNum >= numMessages):
print '\n Invalid Message Number.\n'
else:
break
subject = outboxMessages['sentMessages'][msgNum]['subject'].decode('base64')
message = outboxMessages['sentMessages'][msgNum]['message']#Don't decode since it is done in the saveFile function
subject = subject +'.txt'
saveFile(subject,message)
usrPrompt = 1
main()
elif usrInput == "delete": #will delete a message from the system, not reflected on the UI.
uInput = userInput("Would you like to delete a message from the (I)nbox or (O)utbox?").lower()
if (uInput == 'i' or uInput == 'inbox'):
inboxMessages = json.loads(api.getAllInboxMessages())
numMessages = len(inboxMessages['inboxMessages'])
while True:
msgNum = userInput('Enter the number of the message you wish to delete or (A)ll to empty the inbox.').lower()
if (msgNum == 'a' or msgNum == 'all'):
break
elif (int(msgNum) >= numMessages):
print '\n Invalid Message Number.\n'
else:
break
uInput = userInput("Are you sure, (Y)es or (N)o?").lower()#Prevent accidental deletion
if uInput == "y":
if (msgNum == 'a' or msgNum == 'all'):
print ' '
for msgNum in range (0, numMessages): #processes all of the messages in the inbox
print ' Deleting message ', msgNum+1, ' of ', numMessages
delMsg(0)
print '\n Inbox is empty.'
usrPrompt = 1
else:
delMsg(int(msgNum))
print '\n Notice: Message numbers may have changed.\n'
main()
else:
usrPrompt = 1
elif (uInput == 'o' or uInput == 'outbox'):
outboxMessages = json.loads(api.getAllSentMessages())
numMessages = len(outboxMessages['sentMessages'])
while True:
msgNum = userInput('Enter the number of the message you wish to delete or (A)ll to empty the inbox.').lower()
if (msgNum == 'a' or msgNum == 'all'):
break
elif (int(msgNum) >= numMessages):
print '\n Invalid Message Number.\n'
else:
break
uInput = userInput("Are you sure, (Y)es or (N)o?").lower()#Prevent accidental deletion
if uInput == "y":
if (msgNum == 'a' or msgNum == 'all'):
print ' '
for msgNum in range (0, numMessages): #processes all of the messages in the outbox
print ' Deleting message ', msgNum+1, ' of ', numMessages
delSentMsg(0)
print '\n Outbox is empty.'
usrPrompt = 1
else:
delSentMsg(int(msgNum))
print '\n Notice: Message numbers may have changed.\n'
main()
else:
usrPrompt = 1
else:
print '\n Invalid Entry.\n'
userPrompt = 1
main()
elif usrInput == "exit":
print '\n You are already at the main menu. Use "quit" to quit.\n'
usrPrompt = 1
main()
elif usrInput == "listaddressbookentries":
res = listAddressBookEntries()
if res == 20: print '\n Error: API function not supported.\n'
usrPrompt = 1
main()
elif usrInput == "addaddressbookentry":
address = userInput('Enter address')
label = userInput('Enter label')
res = addAddressToAddressBook(address, label)
if res == 16: print '\n Error: Address already exists in Address Book.\n'
if res == 20: print '\n Error: API function not supported.\n'
usrPrompt = 1
main()
elif usrInput == "deleteaddressbookentry":
address = userInput('Enter address')
res = deleteAddressFromAddressBook(address)
if res == 20: print '\n Error: API function not supported.\n'
usrPrompt = 1
main()
elif usrInput == "markallmessagesread":
markAllMessagesRead()
usrPrompt = 1
main()
elif usrInput == "markallmessagesunread":
markAllMessagesUnread()
usrPrompt = 1
main()
elif usrInput == "status":
clientStatus()
usrPrompt = 1
main()
elif usrInput == "million+":
genMilAddr()
usrPrompt = 1
main()
elif usrInput == "million-":
delMilAddr()
usrPrompt = 1
main()
else:
print '\n "',usrInput,'" is not a command.\n'
usrPrompt = 1
main()
def main():
global api
global usrPrompt
if (usrPrompt == 0):
print '\n ------------------------------'
print ' | Bitmessage Daemon by .dok |'
print ' | Version 0.2.6 for BM 0.3.5 |'
print ' ------------------------------'
api = xmlrpclib.ServerProxy(apiData()) #Connect to BitMessage using these api credentials
if (apiTest() == False):
print '\n ****************************************************************'
print ' WARNING: You are not connected to the Bitmessage client.'
print ' Either Bitmessage is not running or your settings are incorrect.'
print ' Use the command "apiTest" or "bmSettings" to resolve this issue.'
print ' ****************************************************************\n'
print 'Type (H)elp for a list of commands.' #Startup message
usrPrompt = 2
#if (apiTest() == False):#Preform a connection test #taken out until I get the error handler working
# print '*************************************'
# print 'WARNING: No connection to Bitmessage.'
# print '*************************************'
# print ' '
elif (usrPrompt == 1):
print '\nType (H)elp for a list of commands.' #Startup message
usrPrompt = 2
try:
UI((raw_input('>').lower()).replace(" ", ""))
except EOFError:
UI("quit")
if __name__ == "__main__":
main()

View File

@ -23,8 +23,7 @@ import sys
from subprocess import call from subprocess import call
import time import time
from SimpleXMLRPCServer import SimpleXMLRPCServer from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer
from api import MySimpleXMLRPCRequestHandler
from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections
import shared import shared
@ -44,7 +43,10 @@ from debug import logger
# Helper Functions # Helper Functions
import helper_bootstrap import helper_bootstrap
import helper_generic import helper_generic
from helper_threading import *
# singleton lock instance
thisapp = None
def connectToStream(streamNumber): def connectToStream(streamNumber):
shared.streamsInWhichIAmParticipating[streamNumber] = 'no data' shared.streamsInWhichIAmParticipating[streamNumber] = 'no data'
@ -117,13 +119,24 @@ def _fixWinsock():
socket.IPV6_V6ONLY = 27 socket.IPV6_V6ONLY = 27
# This thread, of which there is only one, runs the API. # This thread, of which there is only one, runs the API.
class singleAPI(threading.Thread): class singleAPI(threading.Thread, StoppableThread):
def __init__(self): def __init__(self):
threading.Thread.__init__(self) threading.Thread.__init__(self, name="singleAPI")
self.initStop()
def stopThread(self):
super(singleAPI, self).stopThread()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((shared.config.get('bitmessagesettings', 'apiinterface'), shared.config.getint(
'bitmessagesettings', 'apiport')))
s.shutdown(socket.SHUT_RDWR)
s.close()
except:
pass
def run(self): def run(self):
se = SimpleXMLRPCServer((shared.config.get('bitmessagesettings', 'apiinterface'), shared.config.getint( se = StoppableXMLRPCServer((shared.config.get('bitmessagesettings', 'apiinterface'), shared.config.getint(
'bitmessagesettings', 'apiport')), MySimpleXMLRPCRequestHandler, True, True) 'bitmessagesettings', 'apiport')), MySimpleXMLRPCRequestHandler, True, True)
se.register_introspection_functions() se.register_introspection_functions()
se.serve_forever() se.serve_forever()
@ -139,11 +152,13 @@ if shared.useVeryEasyProofOfWorkForTesting:
class Main: class Main:
def start(self, daemon=False): def start(self, daemon=False):
global thisapp
_fixWinsock() _fixWinsock()
shared.daemon = daemon shared.daemon = daemon
# is the application already running? If yes then exit. # is the application already running? If yes then exit.
thisapp = singleton.singleinstance() thisapp = singleton.singleinstance("", daemon)
# get curses flag # get curses flag
curses = False curses = False
@ -203,6 +218,11 @@ class Main:
singleListenerThread.setup(selfInitiatedConnections) singleListenerThread.setup(selfInitiatedConnections)
singleListenerThread.daemon = True # close the main program even if there are threads left singleListenerThread.daemon = True # close the main program even if there are threads left
singleListenerThread.start() singleListenerThread.start()
if shared.safeConfigGetBoolean('bitmessagesettings','upnp'):
import upnp
upnpThread = upnp.uPnPThread()
upnpThread.start()
if daemon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: if daemon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False:
if curses == False: if curses == False:

View File

@ -1,3 +1,4 @@
from debug import logger
withMessagingMenu = False withMessagingMenu = False
try: try:
from gi.repository import MessagingMenu from gi.repository import MessagingMenu
@ -6,17 +7,38 @@ try:
except ImportError: except ImportError:
MessagingMenu = None MessagingMenu = None
try:
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtNetwork import QLocalSocket, QLocalServer
except Exception as err:
logmsg = 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).'
logger.critical(logmsg, exc_info=True)
sys.exit()
try:
_encoding = QtGui.QApplication.UnicodeUTF8
except AttributeError:
logger.exception('QtGui.QApplication.UnicodeUTF8 error', exc_info=True)
from addresses import * from addresses import *
import shared import shared
from bitmessageui import * from bitmessageui import *
from namecoin import namecoinConnection, ensureNamecoinOptions from namecoin import namecoinConnection, ensureNamecoinOptions
from newaddressdialog import * from newaddressdialog import *
from newaddresswizard import *
from migrationwizard import *
from foldertree import *
from addaddressdialog import * from addaddressdialog import *
from newsubscriptiondialog import * from newsubscriptiondialog import *
from regenerateaddresses import * from regenerateaddresses import *
from newchandialog import * from newchandialog import *
from specialaddressbehavior import * from specialaddressbehavior import *
from emailgateway import *
from settings import * from settings import *
import settingsmixin
from about import * from about import *
from help import * from help import *
from iconglossary import * from iconglossary import *
@ -31,143 +53,29 @@ import pickle
import platform import platform
import textwrap import textwrap
import debug import debug
from debug import logger import random
import subprocess import subprocess
import string
import datetime import datetime
from helper_sql import * from helper_sql import *
import l10n import l10n
import openclpow
try: import types
from PyQt4 import QtCore, QtGui from utils import *
from PyQt4.QtCore import * from collections import OrderedDict
from PyQt4.QtGui import * from account import *
except Exception as err:
print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).'
print 'Error message:', err
sys.exit()
try:
_encoding = QtGui.QApplication.UnicodeUTF8
except AttributeError:
print 'QtGui.QApplication.UnicodeUTF8 error:', err
def _translate(context, text): def _translate(context, text):
return QtGui.QApplication.translate(context, text) return QtGui.QApplication.translate(context, text)
def identiconize(address):
size = 48
# If you include another identicon library, please generate an
# example identicon with the following md5 hash:
# 3fd4bf901b9d4ea1394f0fb358725b28
try:
identicon_lib = shared.config.get('bitmessagesettings', 'identiconlib')
except:
# default to qidenticon_two_x
identicon_lib = 'qidenticon_two_x'
# As an 'identiconsuffix' you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators. (Note however, that E-Mail programs might convert the BM-address to lowercase first.)
# It can be used as a pseudo-password to salt the generation of the identicons to decrease the risk
# of attacks where someone creates an address to mimic someone else's identicon.
identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix')
if not shared.config.getboolean('bitmessagesettings', 'useidenticons'):
idcon = QtGui.QIcon()
return idcon
if (identicon_lib[:len('qidenticon')] == 'qidenticon'):
# print identicon_lib
# originally by:
# :Author:Shin Adachi <shn@glucose.jp>
# Licesensed under FreeBSD License.
# stripped from PIL and uses QT instead (by sendiulo, same license)
import qidenticon
hash = hashlib.md5(addBMIfNotPresent(address)+identiconsuffix).hexdigest()
use_two_colors = (identicon_lib[:len('qidenticon_two')] == 'qidenticon_two')
opacity = int(not((identicon_lib == 'qidenticon_x') | (identicon_lib == 'qidenticon_two_x') | (identicon_lib == 'qidenticon_b') | (identicon_lib == 'qidenticon_two_b')))*255
penwidth = 0
image = qidenticon.render_identicon(int(hash, 16), size, use_two_colors, opacity, penwidth)
# filename = './images/identicons/'+hash+'.png'
# image.save(filename)
idcon = QtGui.QIcon()
idcon.addPixmap(image, QtGui.QIcon.Normal, QtGui.QIcon.Off)
return idcon
elif identicon_lib == 'pydenticon':
# print identicon_lib
# Here you could load pydenticon.py (just put it in the "src" folder of your Bitmessage source)
from pydenticon import Pydenticon
# It is not included in the source, because it is licensed under GPLv3
# GPLv3 is a copyleft license that would influence our licensing
# Find the source here: http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py
# note that it requires PIL to be installed: http://www.pythonware.com/products/pil/
idcon_render = Pydenticon(addBMIfNotPresent(address)+identiconsuffix, size*3)
rendering = idcon_render._render()
data = rendering.convert("RGBA").tostring("raw", "RGBA")
qim = QImage(data, size, size, QImage.Format_ARGB32)
pix = QPixmap.fromImage(qim)
idcon = QtGui.QIcon()
idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off)
return idcon
def avatarize(address):
"""
loads a supported image for the given address' hash form 'avatars' folder
falls back to default avatar if 'default.*' file exists
falls back to identiconize(address)
"""
idcon = QtGui.QIcon()
hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest()
str_broadcast_subscribers = '[Broadcast subscribers]'
if address == str_broadcast_subscribers:
# don't hash [Broadcast subscribers]
hash = address
# http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats
# print QImageReader.supportedImageFormats ()
# QImageReader.supportedImageFormats ()
extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA']
# try to find a specific avatar
for ext in extensions:
lower_hash = shared.appdata + 'avatars/' + hash + '.' + ext.lower()
upper_hash = shared.appdata + 'avatars/' + hash + '.' + ext.upper()
if os.path.isfile(lower_hash):
# print 'found avatar of ', address
idcon.addFile(lower_hash)
return idcon
elif os.path.isfile(upper_hash):
# print 'found avatar of ', address
idcon.addFile(upper_hash)
return idcon
# if we haven't found any, try to find a default avatar
for ext in extensions:
lower_default = shared.appdata + 'avatars/' + 'default.' + ext.lower()
upper_default = shared.appdata + 'avatars/' + 'default.' + ext.upper()
if os.path.isfile(lower_default):
default = lower_default
idcon.addFile(lower_default)
return idcon
elif os.path.isfile(upper_default):
default = upper_default
idcon.addFile(upper_default)
return idcon
# If no avatar is found
return identiconize(address)
def change_translation(locale): def change_translation(locale):
global qtranslator global qtranslator
qtranslator = QtCore.QTranslator() qtranslator = QtCore.QTranslator()
translationpath = os.path.join( translationpath = os.path.join (shared.codePath(), 'translations', 'bitmessage_' + locale)
getattr(sys, '_MEIPASS', sys.path[0]),
'translations',
'bitmessage_' + locale
)
qtranslator.load(translationpath) qtranslator.load(translationpath)
QtGui.QApplication.installTranslator(qtranslator) QtGui.QApplication.installTranslator(qtranslator)
class MyForm(settingsmixin.SMainWindow):
class MyForm(QtGui.QMainWindow):
# sound type constants # sound type constants
SOUND_NONE = 0 SOUND_NONE = 0
@ -183,7 +91,6 @@ class MyForm(QtGui.QMainWindow):
# the maximum frequency of message sounds in seconds # the maximum frequency of message sounds in seconds
maxSoundFrequencySec = 60 maxSoundFrequencySec = 60
str_broadcast_subscribers = '[Broadcast subscribers]'
str_chan = '[chan]' str_chan = '[chan]'
def init_file_menu(self): def init_file_menu(self):
@ -199,13 +106,11 @@ class MyForm(QtGui.QMainWindow):
QtCore.SIGNAL( QtCore.SIGNAL(
"triggered()"), "triggered()"),
self.click_actionRegenerateDeterministicAddresses) self.click_actionRegenerateDeterministicAddresses)
QtCore.QObject.connect(self.ui.actionJoinChan, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.pushButtonAddChan, QtCore.SIGNAL(
"triggered()"), "clicked()"),
self.click_actionJoinChan) # also used for creating chans. self.click_actionJoinChan) # also used for creating chans.
QtCore.QObject.connect(self.ui.pushButtonNewAddress, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.pushButtonNewAddress, QtCore.SIGNAL(
"clicked()"), self.click_NewAddressDialog) "clicked()"), self.click_NewAddressDialog)
QtCore.QObject.connect(self.ui.comboBoxSendFrom, QtCore.SIGNAL(
"activated(int)"), self.redrawLabelFrom)
QtCore.QObject.connect(self.ui.pushButtonAddAddressBook, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.pushButtonAddAddressBook, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonAddAddressBook) "clicked()"), self.click_pushButtonAddAddressBook)
QtCore.QObject.connect(self.ui.pushButtonAddSubscription, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.pushButtonAddSubscription, QtCore.SIGNAL(
@ -216,10 +121,6 @@ class MyForm(QtGui.QMainWindow):
"clicked()"), self.click_pushButtonTTL) "clicked()"), self.click_pushButtonTTL)
QtCore.QObject.connect(self.ui.pushButtonSend, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.pushButtonSend, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonSend) "clicked()"), self.click_pushButtonSend)
QtCore.QObject.connect(self.ui.pushButtonLoadFromAddressBook,
QtCore.SIGNAL(
"clicked()"),
self.click_pushButtonLoadFromAddressBook)
QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonFetchNamecoinID) "clicked()"), self.click_pushButtonFetchNamecoinID)
QtCore.QObject.connect(self.ui.radioButtonBlacklist, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.radioButtonBlacklist, QtCore.SIGNAL(
@ -245,9 +146,16 @@ class MyForm(QtGui.QMainWindow):
_translate( _translate(
"MainWindow", "Add sender to your Address Book"), "MainWindow", "Add sender to your Address Book"),
self.on_action_InboxAddSenderToAddressBook) self.on_action_InboxAddSenderToAddressBook)
self.actionAddSenderToBlackList = self.ui.inboxContextMenuToolbar.addAction(
_translate(
"MainWindow", "Add sender to your Blacklist"),
self.on_action_InboxAddSenderToBlackList)
self.actionTrashInboxMessage = self.ui.inboxContextMenuToolbar.addAction( self.actionTrashInboxMessage = self.ui.inboxContextMenuToolbar.addAction(
_translate("MainWindow", "Move to Trash"), _translate("MainWindow", "Move to Trash"),
self.on_action_InboxTrash) self.on_action_InboxTrash)
self.actionUndeleteTrashedMessage = self.ui.inboxContextMenuToolbar.addAction(
_translate("MainWindow", "Undelete"),
self.on_action_TrashUndelete)
self.actionForceHtml = self.ui.inboxContextMenuToolbar.addAction( self.actionForceHtml = self.ui.inboxContextMenuToolbar.addAction(
_translate( _translate(
"MainWindow", "View HTML code as formatted text"), "MainWindow", "View HTML code as formatted text"),
@ -259,61 +167,97 @@ class MyForm(QtGui.QMainWindow):
self.actionMarkUnread = self.ui.inboxContextMenuToolbar.addAction( self.actionMarkUnread = self.ui.inboxContextMenuToolbar.addAction(
_translate( _translate(
"MainWindow", "Mark Unread"), self.on_action_InboxMarkUnread) "MainWindow", "Mark Unread"), self.on_action_InboxMarkUnread)
# contextmenu messagelists
self.ui.tableWidgetInbox.setContextMenuPolicy( self.ui.tableWidgetInbox.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu) QtCore.Qt.CustomContextMenu)
if connectSignal: if connectSignal:
self.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL( self.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'), 'customContextMenuRequested(const QPoint&)'),
self.on_context_menuInbox) self.on_context_menuInbox)
self.popMenuInbox = QtGui.QMenu(self) self.ui.tableWidgetInboxSubscriptions.setContextMenuPolicy(
self.popMenuInbox.addAction(self.actionForceHtml) QtCore.Qt.CustomContextMenu)
self.popMenuInbox.addAction(self.actionMarkUnread) if connectSignal:
self.popMenuInbox.addSeparator() self.connect(self.ui.tableWidgetInboxSubscriptions, QtCore.SIGNAL(
self.popMenuInbox.addAction(self.actionReply) 'customContextMenuRequested(const QPoint&)'),
self.popMenuInbox.addAction(self.actionAddSenderToAddressBook) self.on_context_menuInbox)
self.popMenuInbox.addSeparator() self.ui.tableWidgetInboxChans.setContextMenuPolicy(
self.popMenuInbox.addAction(self.actionSaveMessageAs) QtCore.Qt.CustomContextMenu)
self.popMenuInbox.addAction(self.actionTrashInboxMessage) if connectSignal:
self.connect(self.ui.tableWidgetInboxChans, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuInbox)
def init_identities_popup_menu(self, connectSignal=True): def init_identities_popup_menu(self, connectSignal=True):
# Popup menu for the Your Identities tab # Popup menu for the Your Identities tab
self.ui.addressContextMenuToolbarYourIdentities = QtGui.QToolBar()
# Actions
self.actionNewYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction(_translate(
"MainWindow", "New"), self.on_action_YourIdentitiesNew)
self.actionEnableYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction(
_translate(
"MainWindow", "Enable"), self.on_action_Enable)
self.actionDisableYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction(
_translate(
"MainWindow", "Disable"), self.on_action_Disable)
self.actionSetAvatarYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction(
_translate(
"MainWindow", "Set avatar..."),
self.on_action_TreeWidgetSetAvatar)
self.actionClipboardYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction(
_translate(
"MainWindow", "Copy address to clipboard"),
self.on_action_Clipboard)
self.actionSpecialAddressBehaviorYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction(
_translate(
"MainWindow", "Special address behavior..."),
self.on_action_SpecialAddressBehaviorDialog)
self.actionEmailGateway = self.ui.addressContextMenuToolbarYourIdentities.addAction(
_translate(
"MainWindow", "Email gateway"),
self.on_action_EmailGatewayDialog)
self.ui.treeWidgetYourIdentities.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuYourIdentities)
def init_chan_popup_menu(self, connectSignal=True):
# Popup menu for the Channels tab
self.ui.addressContextMenuToolbar = QtGui.QToolBar() self.ui.addressContextMenuToolbar = QtGui.QToolBar()
# Actions # Actions
self.actionNew = self.ui.addressContextMenuToolbar.addAction(_translate( self.actionNew = self.ui.addressContextMenuToolbar.addAction(_translate(
"MainWindow", "New"), self.on_action_YourIdentitiesNew) "MainWindow", "New"), self.on_action_YourIdentitiesNew)
self.actionDelete = self.ui.addressContextMenuToolbar.addAction(
_translate("MainWindow", "Delete"),
self.on_action_YourIdentitiesDelete)
self.actionEnable = self.ui.addressContextMenuToolbar.addAction( self.actionEnable = self.ui.addressContextMenuToolbar.addAction(
_translate( _translate(
"MainWindow", "Enable"), self.on_action_YourIdentitiesEnable) "MainWindow", "Enable"), self.on_action_Enable)
self.actionDisable = self.ui.addressContextMenuToolbar.addAction( self.actionDisable = self.ui.addressContextMenuToolbar.addAction(
_translate( _translate(
"MainWindow", "Disable"), self.on_action_YourIdentitiesDisable) "MainWindow", "Disable"), self.on_action_Disable)
self.actionSetAvatar = self.ui.addressContextMenuToolbar.addAction( self.actionSetAvatar = self.ui.addressContextMenuToolbar.addAction(
_translate( _translate(
"MainWindow", "Set avatar..."), "MainWindow", "Set avatar..."),
self.on_action_YourIdentitiesSetAvatar) self.on_action_TreeWidgetSetAvatar)
self.actionClipboard = self.ui.addressContextMenuToolbar.addAction( self.actionClipboard = self.ui.addressContextMenuToolbar.addAction(
_translate( _translate(
"MainWindow", "Copy address to clipboard"), "MainWindow", "Copy address to clipboard"),
self.on_action_YourIdentitiesClipboard) self.on_action_Clipboard)
self.actionSpecialAddressBehavior = self.ui.addressContextMenuToolbar.addAction( self.actionSpecialAddressBehavior = self.ui.addressContextMenuToolbar.addAction(
_translate( _translate(
"MainWindow", "Special address behavior..."), "MainWindow", "Special address behavior..."),
self.on_action_SpecialAddressBehaviorDialog) self.on_action_SpecialAddressBehaviorDialog)
self.ui.tableWidgetYourIdentities.setContextMenuPolicy(
self.ui.treeWidgetChans.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu) QtCore.Qt.CustomContextMenu)
if connectSignal: if connectSignal:
self.connect(self.ui.tableWidgetYourIdentities, QtCore.SIGNAL( self.connect(self.ui.treeWidgetChans, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'), 'customContextMenuRequested(const QPoint&)'),
self.on_context_menuYourIdentities) self.on_context_menuChan)
self.popMenu = QtGui.QMenu(self)
self.popMenu.addAction(self.actionNew)
self.popMenu.addSeparator()
self.popMenu.addAction(self.actionClipboard)
self.popMenu.addSeparator()
self.popMenu.addAction(self.actionEnable)
self.popMenu.addAction(self.actionDisable)
self.popMenu.addAction(self.actionSetAvatar)
self.popMenu.addAction(self.actionSpecialAddressBehavior)
def init_addressbook_popup_menu(self, connectSignal=True): def init_addressbook_popup_menu(self, connectSignal=True):
# Popup menu for the Address Book page # Popup menu for the Address Book page
@ -347,14 +291,6 @@ class MyForm(QtGui.QMainWindow):
self.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL( self.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'), 'customContextMenuRequested(const QPoint&)'),
self.on_context_menuAddressBook) self.on_context_menuAddressBook)
self.popMenuAddressBook = QtGui.QMenu(self)
self.popMenuAddressBook.addAction(self.actionAddressBookSend)
self.popMenuAddressBook.addAction(self.actionAddressBookClipboard)
self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe)
self.popMenuAddressBook.addAction(self.actionAddressBookSetAvatar)
self.popMenuAddressBook.addSeparator()
self.popMenuAddressBook.addAction(self.actionAddressBookNew)
self.popMenuAddressBook.addAction(self.actionAddressBookDelete)
def init_subscriptions_popup_menu(self, connectSignal=True): def init_subscriptions_popup_menu(self, connectSignal=True):
# Popup menu for the Subscriptions page # Popup menu for the Subscriptions page
@ -376,22 +312,13 @@ class MyForm(QtGui.QMainWindow):
self.on_action_SubscriptionsDisable) self.on_action_SubscriptionsDisable)
self.actionsubscriptionsSetAvatar = self.ui.subscriptionsContextMenuToolbar.addAction( self.actionsubscriptionsSetAvatar = self.ui.subscriptionsContextMenuToolbar.addAction(
_translate("MainWindow", "Set avatar..."), _translate("MainWindow", "Set avatar..."),
self.on_action_SubscriptionsSetAvatar) self.on_action_TreeWidgetSetAvatar)
self.ui.tableWidgetSubscriptions.setContextMenuPolicy( self.ui.treeWidgetSubscriptions.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu) QtCore.Qt.CustomContextMenu)
if connectSignal: if connectSignal:
self.connect(self.ui.tableWidgetSubscriptions, QtCore.SIGNAL( self.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'), 'customContextMenuRequested(const QPoint&)'),
self.on_context_menuSubscriptions) self.on_context_menuSubscriptions)
self.popMenuSubscriptions = QtGui.QMenu(self)
self.popMenuSubscriptions.addAction(self.actionsubscriptionsNew)
self.popMenuSubscriptions.addAction(self.actionsubscriptionsDelete)
self.popMenuSubscriptions.addSeparator()
self.popMenuSubscriptions.addAction(self.actionsubscriptionsEnable)
self.popMenuSubscriptions.addAction(self.actionsubscriptionsDisable)
self.popMenuSubscriptions.addAction(self.actionsubscriptionsSetAvatar)
self.popMenuSubscriptions.addSeparator()
self.popMenuSubscriptions.addAction(self.actionsubscriptionsClipboard)
def init_sent_popup_menu(self, connectSignal=True): def init_sent_popup_menu(self, connectSignal=True):
# Popup menu for the Sent page # Popup menu for the Sent page
@ -407,12 +334,6 @@ class MyForm(QtGui.QMainWindow):
self.actionForceSend = self.ui.sentContextMenuToolbar.addAction( self.actionForceSend = self.ui.sentContextMenuToolbar.addAction(
_translate( _translate(
"MainWindow", "Force send"), self.on_action_ForceSend) "MainWindow", "Force send"), self.on_action_ForceSend)
self.ui.tableWidgetSent.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.tableWidgetSent, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuSent)
# self.popMenuSent = QtGui.QMenu( self ) # self.popMenuSent = QtGui.QMenu( self )
# self.popMenuSent.addAction( self.actionSentClipboard ) # self.popMenuSent.addAction( self.actionSentClipboard )
# self.popMenuSent.addAction( self.actionTrashSentMessage ) # self.popMenuSent.addAction( self.actionTrashSentMessage )
@ -457,6 +378,198 @@ class MyForm(QtGui.QMainWindow):
self.popMenuBlacklist.addAction(self.actionBlacklistDisable) self.popMenuBlacklist.addAction(self.actionBlacklistDisable)
self.popMenuBlacklist.addAction(self.actionBlacklistSetAvatar) self.popMenuBlacklist.addAction(self.actionBlacklistSetAvatar)
def rerenderTabTreeSubscriptions(self):
treeWidget = self.ui.treeWidgetSubscriptions
folders = Ui_FolderWidget.folderWeight.keys()
folders.remove("new")
# sort ascending when creating
if treeWidget.topLevelItemCount() == 0:
treeWidget.header().setSortIndicator(0, Qt.AscendingOrder)
# init dictionary
db = getSortedSubscriptions(True)
for address in db:
for folder in folders:
if not folder in db[address]:
db[address][folder] = {}
if treeWidget.isSortingEnabled():
treeWidget.setSortingEnabled(False)
widgets = {}
i = 0
while i < treeWidget.topLevelItemCount():
widget = treeWidget.topLevelItem(i)
if widget is not None:
toAddress = widget.address
else:
toAddress = None
if not toAddress in db:
treeWidget.takeTopLevelItem(i)
# no increment
continue
unread = 0
j = 0
while j < widget.childCount():
subwidget = widget.child(j)
try:
subwidget.setUnreadCount(db[toAddress][subwidget.folderName]['count'])
unread += db[toAddress][subwidget.folderName]['count']
db[toAddress].pop(subwidget.folderName, None)
except:
widget.takeChild(j)
# no increment
continue
j += 1
# add missing folders
if len(db[toAddress]) > 0:
j = 0
for f, c in db[toAddress].iteritems():
try:
subwidget = Ui_FolderWidget(widget, j, toAddress, f, c['count'])
except KeyError:
subwidget = Ui_FolderWidget(widget, j, toAddress, f, 0)
j += 1
widget.setUnreadCount(unread)
db.pop(toAddress, None)
i += 1
i = 0
for toAddress in db:
widget = Ui_SubscriptionWidget(treeWidget, i, toAddress, db[toAddress]["inbox"]['count'], db[toAddress]["inbox"]['label'], db[toAddress]["inbox"]['enabled'])
j = 0
unread = 0
for folder in folders:
try:
subwidget = Ui_FolderWidget(widget, j, toAddress, folder, db[toAddress][folder]['count'])
unread += db[toAddress][folder]['count']
except KeyError:
subwidget = Ui_FolderWidget(widget, j, toAddress, folder, 0)
j += 1
widget.setUnreadCount(unread)
widget.setFlags (widget.flags() | QtCore.Qt.ItemIsEditable)
i += 1
treeWidget.setSortingEnabled(True)
def rerenderTabTreeMessages(self):
self.rerenderTabTree('messages')
def rerenderTabTreeChans(self):
self.rerenderTabTree('chan')
def rerenderTabTree(self, tab):
if tab == 'messages':
treeWidget = self.ui.treeWidgetYourIdentities
elif tab == 'chan':
treeWidget = self.ui.treeWidgetChans
folders = Ui_FolderWidget.folderWeight.keys()
# sort ascending when creating
if treeWidget.topLevelItemCount() == 0:
treeWidget.header().setSortIndicator(0, Qt.AscendingOrder)
# init dictionary
db = {}
enabled = {}
for toAddress in getSortedAccounts():
isEnabled = shared.config.getboolean(
toAddress, 'enabled')
isChan = shared.safeConfigGetBoolean(
toAddress, 'chan')
isMaillinglist = shared.safeConfigGetBoolean(
toAddress, 'mailinglist')
if tab == 'messages':
if isChan:
continue
elif tab == 'chan':
if not isChan:
continue
db[toAddress] = {}
for folder in folders:
db[toAddress][folder] = 0
enabled[toAddress] = isEnabled
# get number of (unread) messages
total = 0
queryreturn = sqlQuery('SELECT toaddress, folder, count(msgid) as cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder')
for row in queryreturn:
toaddress, folder, cnt = row
total += cnt
if toaddress in db and folder in db[toaddress]:
db[toaddress][folder] = cnt
if tab == "messages":
db[None] = {}
db[None]["inbox"] = total
db[None]["new"] = total
enabled[None] = True
if treeWidget.isSortingEnabled():
treeWidget.setSortingEnabled(False)
widgets = {}
i = 0
while i < treeWidget.topLevelItemCount():
widget = treeWidget.topLevelItem(i)
if widget is not None:
toAddress = widget.address
else:
toAddress = None
if not toAddress in db:
treeWidget.takeTopLevelItem(i)
# no increment
continue
unread = 0
j = 0
while j < widget.childCount():
subwidget = widget.child(j)
try:
subwidget.setUnreadCount(db[toAddress][subwidget.folderName])
unread += db[toAddress][subwidget.folderName]
db[toAddress].pop(subwidget.folderName, None)
except:
widget.takeChild(j)
# no increment
continue
j += 1
# add missing folders
if len(db[toAddress]) > 0:
j = 0
for f, c in db[toAddress].iteritems():
subwidget = Ui_FolderWidget(widget, j, toAddress, f, c)
j += 1
widget.setUnreadCount(unread)
db.pop(toAddress, None)
i += 1
i = 0
for toAddress in db:
widget = Ui_AddressWidget(treeWidget, i, toAddress, db[toAddress]["inbox"], enabled[toAddress])
j = 0
unread = 0
for folder in folders:
if toAddress is not None and folder == "new":
continue
if toAddress is None and folder in ["trash", "sent"]:
continue
subwidget = Ui_FolderWidget(widget, j, toAddress, folder, db[toAddress][folder])
unread += db[toAddress][folder]
j += 1
widget.setUnreadCount(unread)
widget.setFlags (widget.flags() | QtCore.Qt.ItemIsEditable)
i += 1
treeWidget.setSortingEnabled(True)
def __init__(self, parent=None): def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent) QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow() self.ui = Ui_MainWindow()
@ -464,20 +577,18 @@ class MyForm(QtGui.QMainWindow):
# Ask the user if we may delete their old version 1 addresses if they # Ask the user if we may delete their old version 1 addresses if they
# have any. # have any.
configSections = shared.config.sections() for addressInKeysFile in getSortedAccounts():
for addressInKeysFile in configSections: status, addressVersionNumber, streamNumber, hash = decodeAddress(
if addressInKeysFile != 'bitmessagesettings': addressInKeysFile)
status, addressVersionNumber, streamNumber, hash = decodeAddress( if addressVersionNumber == 1:
addressInKeysFile) displayMsg = _translate(
if addressVersionNumber == 1: "MainWindow", "One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. "
displayMsg = _translate( + "May we delete it now?").arg(addressInKeysFile)
"MainWindow", "One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. " reply = QtGui.QMessageBox.question(
+ "May we delete it now?").arg(addressInKeysFile) self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
reply = QtGui.QMessageBox.question( if reply == QtGui.QMessageBox.Yes:
self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) shared.config.remove_section(addressInKeysFile)
if reply == QtGui.QMessageBox.Yes: shared.writeKeysFile()
shared.config.remove_section(addressInKeysFile)
shared.writeKeysFile()
# Configure Bitmessage to start on startup (or remove the # Configure Bitmessage to start on startup (or remove the
# configuration) based on the setting in the keys.dat file # configuration) based on the setting in the keys.dat file
@ -500,59 +611,40 @@ class MyForm(QtGui.QMainWindow):
self.totalNumberOfBytesReceived = 0 self.totalNumberOfBytesReceived = 0
self.totalNumberOfBytesSent = 0 self.totalNumberOfBytesSent = 0
self.ui.labelSendBroadcastWarning.setVisible(False)
self.timer = QtCore.QTimer() self.timer = QtCore.QTimer()
self.timer.start(2000) # milliseconds self.timer.start(2000) # milliseconds
QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds) QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds)
# e.g. for editing labels
self.recurDepth = 0
# switch back to this when replying
self.replyFromTab = None
self.init_file_menu() self.init_file_menu()
self.init_inbox_popup_menu() self.init_inbox_popup_menu()
self.init_identities_popup_menu() self.init_identities_popup_menu()
self.init_addressbook_popup_menu() self.init_addressbook_popup_menu()
self.init_subscriptions_popup_menu() self.init_subscriptions_popup_menu()
self.init_chan_popup_menu()
self.init_sent_popup_menu() self.init_sent_popup_menu()
self.init_blacklist_popup_menu() self.init_blacklist_popup_menu()
# Initialize the user's list of addresses on the 'Chan' tab.
self.rerenderTabTreeChans()
# Initialize the user's list of addresses on the 'Your Identities' tab. # Initialize the user's list of addresses on the 'Messages' tab.
configSections = shared.config.sections() self.rerenderTabTreeMessages()
for addressInKeysFile in configSections:
if addressInKeysFile != 'bitmessagesettings':
isEnabled = shared.config.getboolean(
addressInKeysFile, 'enabled')
newItem = QtGui.QTableWidgetItem(unicode(
shared.config.get(addressInKeysFile, 'label'), 'utf-8)'))
if not isEnabled:
newItem.setTextColor(QtGui.QColor(128, 128, 128))
self.ui.tableWidgetYourIdentities.insertRow(0)
newItem.setIcon(avatarize(addressInKeysFile))
self.ui.tableWidgetYourIdentities.setItem(0, 0, newItem)
newItem = QtGui.QTableWidgetItem(addressInKeysFile)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if shared.safeConfigGetBoolean(addressInKeysFile, 'chan'):
newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange
if not isEnabled:
newItem.setTextColor(QtGui.QColor(128, 128, 128))
if shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'):
newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta
self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem)
newItem = QtGui.QTableWidgetItem(str(
decodeAddress(addressInKeysFile)[2]))
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not isEnabled:
newItem.setTextColor(QtGui.QColor(128, 128, 128))
self.ui.tableWidgetYourIdentities.setItem(0, 2, newItem)
if isEnabled:
status, addressVersionNumber, streamNumber, hash = decodeAddress(
addressInKeysFile)
# Load inbox from messages database file # Set welcome message
self.loadInbox() self.ui.textEditInboxMessage.setText(
"""
# Load Sent items from database Welcome to easy and secure Bitmessage
self.loadSent() * send messages to other people
* send broadcast messages like twitter or
* discuss in chan(nel)s with other people
"""
)
# Initialize the address book # Initialize the address book
self.rerenderAddressBook() self.rerenderAddressBook()
@ -563,10 +655,10 @@ class MyForm(QtGui.QMainWindow):
# Initialize the inbox search # Initialize the inbox search
QtCore.QObject.connect(self.ui.inboxSearchLineEdit, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.inboxSearchLineEdit, QtCore.SIGNAL(
"returnPressed()"), self.inboxSearchLineEditPressed) "returnPressed()"), self.inboxSearchLineEditPressed)
QtCore.QObject.connect(self.ui.inboxSearchLineEditSubscriptions, QtCore.SIGNAL(
# Initialize the sent search "returnPressed()"), self.inboxSearchLineEditPressed)
QtCore.QObject.connect(self.ui.sentSearchLineEdit, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.inboxSearchLineEditChans, QtCore.SIGNAL(
"returnPressed()"), self.sentSearchLineEditPressed) "returnPressed()"), self.inboxSearchLineEditPressed)
# Initialize the Blacklist or Whitelist # Initialize the Blacklist or Whitelist
if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'white': if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'white':
@ -574,16 +666,31 @@ class MyForm(QtGui.QMainWindow):
self.ui.radioButtonWhitelist.click() self.ui.radioButtonWhitelist.click()
self.rerenderBlackWhiteList() self.rerenderBlackWhiteList()
QtCore.QObject.connect(self.ui.tableWidgetYourIdentities, QtCore.SIGNAL( # Initialize addressbook
"itemChanged(QTableWidgetItem *)"), self.tableWidgetYourIdentitiesItemChanged)
QtCore.QObject.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL(
"itemChanged(QTableWidgetItem *)"), self.tableWidgetAddressBookItemChanged) "itemChanged(QTableWidgetItem *)"), self.tableWidgetAddressBookItemChanged)
QtCore.QObject.connect(self.ui.tableWidgetSubscriptions, QtCore.SIGNAL(
"itemChanged(QTableWidgetItem *)"), self.tableWidgetSubscriptionsItemChanged) # show messages from message list
QtCore.QObject.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL(
"itemSelectionChanged ()"), self.tableWidgetInboxItemClicked) "itemSelectionChanged ()"), self.tableWidgetInboxItemClicked)
QtCore.QObject.connect(self.ui.tableWidgetSent, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.tableWidgetInboxSubscriptions, QtCore.SIGNAL(
"itemSelectionChanged ()"), self.tableWidgetSentItemClicked) "itemSelectionChanged ()"), self.tableWidgetInboxItemClicked)
QtCore.QObject.connect(self.ui.tableWidgetInboxChans, QtCore.SIGNAL(
"itemSelectionChanged ()"), self.tableWidgetInboxItemClicked)
# tree address lists
QtCore.QObject.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL(
"itemSelectionChanged ()"), self.treeWidgetItemClicked)
QtCore.QObject.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL(
"itemChanged (QTreeWidgetItem *, int)"), self.treeWidgetItemChanged)
QtCore.QObject.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL(
"itemSelectionChanged ()"), self.treeWidgetItemClicked)
QtCore.QObject.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL(
"itemChanged (QTreeWidgetItem *, int)"), self.treeWidgetItemChanged)
QtCore.QObject.connect(self.ui.treeWidgetChans, QtCore.SIGNAL(
"itemSelectionChanged ()"), self.treeWidgetItemClicked)
QtCore.QObject.connect(self.ui.treeWidgetChans, QtCore.SIGNAL(
"itemChanged (QTreeWidgetItem *, int)"), self.treeWidgetItemChanged)
# Put the colored icon on the status bar # Put the colored icon on the status bar
# self.ui.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png")) # self.ui.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png"))
@ -594,13 +701,14 @@ class MyForm(QtGui.QMainWindow):
self.numberOfMessagesProcessed = 0 self.numberOfMessagesProcessed = 0
self.numberOfBroadcastsProcessed = 0 self.numberOfBroadcastsProcessed = 0
self.numberOfPubkeysProcessed = 0 self.numberOfPubkeysProcessed = 0
self.unreadCount = 0
# Set the icon sizes for the identicons # Set the icon sizes for the identicons
identicon_size = 3*7 identicon_size = 3*7
self.ui.tableWidgetInbox.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetInbox.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.ui.tableWidgetSent.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.treeWidgetChans.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.ui.tableWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.treeWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.ui.tableWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.treeWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.ui.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size))
@ -641,6 +749,8 @@ class MyForm(QtGui.QMainWindow):
"rerenderBlackWhiteList()"), self.rerenderBlackWhiteList) "rerenderBlackWhiteList()"), self.rerenderBlackWhiteList)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"removeInboxRowByMsgid(PyQt_PyObject)"), self.removeInboxRowByMsgid) "removeInboxRowByMsgid(PyQt_PyObject)"), self.removeInboxRowByMsgid)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"newVersionAvailable(PyQt_PyObject)"), self.newVersionAvailable)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"displayAlert(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayAlert) "displayAlert(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayAlert)
self.UISignalThread.start() self.UISignalThread.start()
@ -649,6 +759,7 @@ class MyForm(QtGui.QMainWindow):
# structures were initialized. # structures were initialized.
self.rerenderComboBoxSendFrom() self.rerenderComboBoxSendFrom()
self.rerenderComboBoxSendFromBroadcast()
# Put the TTL slider in the correct spot # Put the TTL slider in the correct spot
TTL = shared.config.getint('bitmessagesettings', 'ttl') TTL = shared.config.getint('bitmessagesettings', 'ttl')
@ -661,7 +772,9 @@ class MyForm(QtGui.QMainWindow):
QtCore.QObject.connect(self.ui.horizontalSliderTTL, QtCore.SIGNAL( QtCore.QObject.connect(self.ui.horizontalSliderTTL, QtCore.SIGNAL(
"valueChanged(int)"), self.updateTTL) "valueChanged(int)"), self.updateTTL)
self.initSettings()
# Check to see whether we can connect to namecoin. Hide the 'Fetch Namecoin ID' button if we can't. # Check to see whether we can connect to namecoin. Hide the 'Fetch Namecoin ID' button if we can't.
try: try:
options = {} options = {}
@ -674,7 +787,7 @@ class MyForm(QtGui.QMainWindow):
if nc.test()[0] == 'failed': if nc.test()[0] == 'failed':
self.ui.pushButtonFetchNamecoinID.hide() self.ui.pushButtonFetchNamecoinID.hide()
except: except:
print 'There was a problem testing for a Namecoin daemon. Hiding the Fetch Namecoin ID button' logger.error('There was a problem testing for a Namecoin daemon. Hiding the Fetch Namecoin ID button')
self.ui.pushButtonFetchNamecoinID.hide() self.ui.pushButtonFetchNamecoinID.hide()
def updateTTL(self, sliderPosition): def updateTTL(self, sliderPosition):
@ -700,10 +813,6 @@ class MyForm(QtGui.QMainWindow):
if not self.actionShow.isChecked(): if not self.actionShow.isChecked():
self.hide() self.hide()
else: else:
if sys.platform[0:3] == 'win':
self.setWindowFlags(Qt.Window)
# else:
# self.showMaximized()
self.show() self.show()
self.setWindowState( self.setWindowState(
self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
@ -781,250 +890,306 @@ class MyForm(QtGui.QMainWindow):
# Show the program window and select subscriptions tab # Show the program window and select subscriptions tab
def appIndicatorSubscribe(self): def appIndicatorSubscribe(self):
self.appIndicatorShow() self.appIndicatorShow()
self.ui.tabWidget.setCurrentIndex(4) self.ui.tabWidget.setCurrentIndex(2)
# Show the program window and select the address book tab # Show the program window and select channels tab
def appIndicatorAddressBook(self): def appIndicatorChannel(self):
self.appIndicatorShow() self.appIndicatorShow()
self.ui.tabWidget.setCurrentIndex(5) self.ui.tabWidget.setCurrentIndex(3)
def propagateUnreadCount(self, address = None, folder = "inbox", widget = None, type = 1):
def updateUnreadCount(item):
# if refreshing the account root, we need to rescan folders
if type == 0 or (folder is None and isinstance(item, Ui_FolderWidget)):
if addressItem.type in [AccountMixin.SUBSCRIPTION, AccountMixin.MAILINGLIST]:
xAddress = "fromaddress"
else:
xAddress = "toaddress"
xFolder = folder
if isinstance(item, Ui_FolderWidget):
xFolder = item.folderName
if address and xFolder:
queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE " + xAddress + " = ? AND folder = ? AND read = 0", address, xFolder)
elif address:
queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE " + xAddress + " = ? AND read = 0", address)
elif xFolder:
queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE folder = ? AND read = 0", xFolder)
else:
queryreturn = sqlQuery("SELECT COUNT(*) FROM inbox WHERE read = 0")
for row in queryreturn:
item.setUnreadCount(int(row[0]))
if isinstance(item, Ui_AddressWidget) and item.type == AccountMixin.ALL:
self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount())
elif type == 1:
item.setUnreadCount(item.unreadCount + 1)
if isinstance(item, Ui_AddressWidget) and item.type == AccountMixin.ALL:
self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount + 1))
elif type == -1:
item.setUnreadCount(item.unreadCount - 1)
if isinstance(item, Ui_AddressWidget) and item.type == AccountMixin.ALL:
self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount(self.unreadCount -1))
widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans]
# FIXME this is a hack
if folder == "new":
folder = "inbox"
for treeWidget in widgets:
root = treeWidget.invisibleRootItem()
for i in range(root.childCount()):
addressItem = root.child(i)
if addressItem.type != AccountMixin.ALL and address is not None and addressItem.data(0, QtCore.Qt.UserRole) != address:
continue
updateUnreadCount(addressItem)
if addressItem.childCount == 0:
continue
for j in range(addressItem.childCount()):
folderItem = addressItem.child(j)
if folder is not None and folderItem.folderName != folder and addressItem.type != AccountMixin.ALL:
continue
updateUnreadCount(folderItem)
def addMessageListItem(self, tableWidget, items):
tableWidget.insertRow(0)
for i in range(len(items)):
tableWidget.setItem(0, i, items[i])
def addMessageListItemSent(self, tableWidget, toAddress, fromAddress, subject, status, ackdata, lastactiontime):
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
acct = accountClass(fromAddress)
acct.parseMessage(toAddress, fromAddress, subject, "")
items = []
toAddressItem = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8'))
toAddressItem.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")")
toAddressItem.setIcon(avatarize(toAddress))
toAddressItem.setData(Qt.UserRole, str(toAddress))
toAddressItem.setTextColor(AccountColor(toAddress).accountColor())
toAddressItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
items.append(toAddressItem)
fromAddressItem = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8'))
fromAddressItem.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(acct.fromAddress) + ")")
fromAddressItem.setIcon(avatarize(fromAddress))
fromAddressItem.setData(Qt.UserRole, str(fromAddress))
fromAddressItem.setTextColor(AccountColor(fromAddress).accountColor())
fromAddressItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
items.append(fromAddressItem)
subjectItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8'))
subjectItem.setToolTip(unicode(acct.subject, 'utf-8'))
subjectItem.setData(Qt.UserRole, str(subject))
subjectItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
items.append(subjectItem)
if status == 'awaitingpubkey':
statusText = _translate(
"MainWindow", "Waiting for their encryption key. Will request it again soon.")
elif status == 'doingpowforpubkey':
statusText = _translate(
"MainWindow", "Encryption key request queued.")
elif status == 'msgqueued':
statusText = _translate(
"MainWindow", "Queued.")
elif status == 'msgsent':
statusText = _translate("MainWindow", "Message sent. Waiting for acknowledgement. Sent at %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'msgsentnoackexpected':
statusText = _translate("MainWindow", "Message sent. Sent at %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'doingmsgpow':
statusText = _translate(
"MainWindow", "Need to do work to send message. Work is queued.")
elif status == 'ackreceived':
statusText = _translate("MainWindow", "Acknowledgement of the message received %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'broadcastqueued':
statusText = _translate(
"MainWindow", "Broadcast queued.")
elif status == 'broadcastsent':
statusText = _translate("MainWindow", "Broadcast on %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'toodifficult':
statusText = _translate("MainWindow", "Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'badkey':
statusText = _translate("MainWindow", "Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'forcepow':
statusText = _translate(
"MainWindow", "Forced difficulty override. Send should start soon.")
else:
statusText = _translate("MainWindow", "Unknown status: %1 %2").arg(status).arg(
l10n.formatTimestamp(lastactiontime))
newItem = myTableWidgetItem(statusText)
newItem.setToolTip(statusText)
newItem.setData(Qt.UserRole, QByteArray(ackdata))
newItem.setData(33, int(lastactiontime))
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
items.append(newItem)
self.addMessageListItem(tableWidget, items)
return acct
def addMessageListItemInbox(self, tableWidget, msgfolder, msgid, toAddress, fromAddress, subject, received, read):
font = QFont()
font.setBold(True)
if tableWidget == self.ui.tableWidgetInboxSubscriptions:
acct = accountClass(fromAddress)
else:
acct = accountClass(toAddress)
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
acct.parseMessage(toAddress, fromAddress, subject, "")
items = []
#to
to_item = QtGui.QTableWidgetItem(unicode(acct.toLabel, 'utf-8'))
to_item.setToolTip(unicode(acct.toLabel, 'utf-8') + " (" + str(acct.toAddress) + ")")
to_item.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not read:
to_item.setFont(font)
to_item.setData(Qt.UserRole, str(toAddress))
to_item.setTextColor(AccountColor(toAddress).accountColor())
to_item.setIcon(avatarize(toAddress))
items.append(to_item)
# from
from_item = QtGui.QTableWidgetItem(unicode(acct.fromLabel, 'utf-8'))
from_item.setToolTip(unicode(acct.fromLabel, 'utf-8') + " (" + str(fromAddress) + ")")
from_item.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not read:
from_item.setFont(font)
from_item.setData(Qt.UserRole, str(fromAddress))
from_item.setTextColor(AccountColor(fromAddress).accountColor())
from_item.setIcon(avatarize(fromAddress))
items.append(from_item)
# subject
subject_item = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8'))
subject_item.setToolTip(unicode(acct.subject, 'utf-8'))
subject_item.setData(Qt.UserRole, str(subject))
subject_item.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not read:
subject_item.setFont(font)
items.append(subject_item)
# time received
time_item = myTableWidgetItem(l10n.formatTimestamp(received))
time_item.setToolTip(l10n.formatTimestamp(received))
time_item.setData(Qt.UserRole, QByteArray(msgid))
time_item.setData(33, int(received))
time_item.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not read:
time_item.setFont(font)
items.append(time_item)
self.addMessageListItem(tableWidget, items)
return acct
# Load Sent items from database # Load Sent items from database
def loadSent(self, where="", what=""): def loadSent(self, tableWidget, account, where="", what=""):
what = "%" + what + "%" what = "%" + what + "%"
if where == "To": if where == _translate("MainWindow", "To"):
where = "toaddress" where = "toaddress"
elif where == "From": elif where == _translate("MainWindow", "From"):
where = "fromaddress" where = "fromaddress"
elif where == "Subject": elif where == _translate("MainWindow", "Subject"):
where = "subject" where = "subject"
elif where == "Message": elif where == _translate("MainWindow", "Message"):
where = "message" where = "message"
else: else:
where = "toaddress || fromaddress || subject || message" where = "toaddress || fromaddress || subject || message"
tableWidget.setSortingEnabled(False)
if tableWidget == self.ui.tableWidgetInboxChans or tableWidget == self.ui.tableWidgetInboxSubscriptions:
tableWidget.setColumnHidden(0, True)
tableWidget.setColumnHidden(1, False)
xAddress = 'toaddress'
else:
tableWidget.setColumnHidden(0, False)
tableWidget.setColumnHidden(1, True)
xAddress = 'fromaddress'
sqlStatement = ''' sqlStatement = '''
SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
FROM sent WHERE folder="sent" AND %s LIKE ? FROM sent WHERE ''' + xAddress + '''=? AND folder="sent" AND %s LIKE ?
ORDER BY lastactiontime ORDER BY lastactiontime
''' % (where,) ''' % (where,)
while self.ui.tableWidgetSent.rowCount() > 0: tableWidget.setRowCount(0)
self.ui.tableWidgetSent.removeRow(0) acct = None
queryreturn = sqlQuery(sqlStatement, account, what)
queryreturn = sqlQuery(sqlStatement, what)
for row in queryreturn: for row in queryreturn:
toAddress, fromAddress, subject, status, ackdata, lastactiontime = row toAddress, fromAddress, subject, status, ackdata, lastactiontime = row
subject = shared.fixPotentiallyInvalidUTF8Data(subject) self.addMessageListItemSent(tableWidget, toAddress, fromAddress, subject, status, ackdata, lastactiontime)
if shared.config.has_section(fromAddress): tableWidget.setSortingEnabled(False)
fromLabel = shared.config.get(fromAddress, 'label') tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder)
tableWidget.keyPressEvent = self.tableWidgetSentKeyPressEvent
# Load messages from database file
def loadMessagelist(self, tableWidget, account, folder="inbox", where="", what="", unreadOnly = False):
if folder == 'sent':
self.loadSent(tableWidget, account, where, what)
return
if what != "":
what = "%" + what + "%"
if where == _translate("MainWindow", "To"):
where = "toaddress"
elif where == _translate("MainWindow", "From"):
where = "fromaddress"
elif where == _translate("MainWindow", "Subject"):
where = "subject"
elif where == _translate("MainWindow", "Message"):
where = "message"
else: else:
fromLabel = fromAddress where = "toaddress || fromaddress || subject || message"
toLabel = ''
queryreturn = sqlQuery(
'''select label from addressbook where address=?''', toAddress)
if queryreturn != []:
for row in queryreturn:
toLabel, = row
if toLabel == '':
# It might be a broadcast message. We should check for that
# label.
queryreturn = sqlQuery(
'''select label from subscriptions where address=?''', toAddress)
if queryreturn != []:
for row in queryreturn:
toLabel, = row
if toLabel == '':
if shared.config.has_section(toAddress):
toLabel = shared.config.get(toAddress, 'label')
if toLabel == '':
toLabel = toAddress
self.ui.tableWidgetSent.insertRow(0)
toAddressItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8'))
toAddressItem.setToolTip(unicode(toLabel, 'utf-8'))
toAddressItem.setIcon(avatarize(toAddress))
toAddressItem.setData(Qt.UserRole, str(toAddress))
toAddressItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.ui.tableWidgetSent.setItem(0, 0, toAddressItem)
if fromLabel == '':
fromLabel = fromAddress
fromAddressItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8'))
fromAddressItem.setToolTip(unicode(fromLabel, 'utf-8'))
fromAddressItem.setIcon(avatarize(fromAddress))
fromAddressItem.setData(Qt.UserRole, str(fromAddress))
fromAddressItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.ui.tableWidgetSent.setItem(0, 1, fromAddressItem)
subjectItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8'))
subjectItem.setToolTip(unicode(subject, 'utf-8'))
subjectItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.ui.tableWidgetSent.setItem(0, 2, subjectItem)
if status == 'awaitingpubkey':
statusText = _translate(
"MainWindow", "Waiting for their encryption key. Will request it again soon.")
elif status == 'doingpowforpubkey':
statusText = _translate(
"MainWindow", "Encryption key request queued.")
elif status == 'msgqueued':
statusText = _translate(
"MainWindow", "Queued.")
elif status == 'msgsent':
statusText = _translate("MainWindow", "Message sent. Waiting for acknowledgement. Sent at %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'msgsentnoackexpected':
statusText = _translate("MainWindow", "Message sent. Sent at %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'doingmsgpow':
statusText = _translate(
"MainWindow", "Need to do work to send message. Work is queued.")
elif status == 'ackreceived':
statusText = _translate("MainWindow", "Acknowledgement of the message received %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'broadcastqueued':
statusText = _translate(
"MainWindow", "Broadcast queued.")
elif status == 'broadcastsent':
statusText = _translate("MainWindow", "Broadcast on %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'toodifficult':
statusText = _translate("MainWindow", "Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'badkey':
statusText = _translate("MainWindow", "Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'forcepow':
statusText = _translate(
"MainWindow", "Forced difficulty override. Send should start soon.")
else:
statusText = _translate("MainWindow", "Unknown status: %1 %2").arg(status).arg(
l10n.formatTimestamp(lastactiontime))
newItem = myTableWidgetItem(statusText)
newItem.setToolTip(statusText)
newItem.setData(Qt.UserRole, QByteArray(ackdata))
newItem.setData(33, int(lastactiontime))
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.ui.tableWidgetSent.setItem(0, 3, newItem)
self.ui.tableWidgetSent.sortItems(3, Qt.DescendingOrder)
self.ui.tableWidgetSent.keyPressEvent = self.tableWidgetSentKeyPressEvent
# Load inbox from messages database file
def loadInbox(self, where="", what=""):
what = "%" + what + "%"
if where == "To":
where = "toaddress"
elif where == "From":
where = "fromaddress"
elif where == "Subject":
where = "subject"
elif where == "Message":
where = "message"
else: else:
where = "toaddress || fromaddress || subject || message" what = None
if tableWidget == self.ui.tableWidgetInboxSubscriptions:
xAddress = "fromaddress"
else:
xAddress = "toaddress"
sqlStatementBase = '''SELECT folder, msgid, toaddress, fromaddress, subject, received, read
FROM inbox '''
sqlStatementParts = []
sqlArguments = []
if account is not None:
sqlStatementParts.append(xAddress + " = ? ")
sqlArguments.append(account)
if folder is not None:
sqlStatementParts.append("folder = ? ")
sqlArguments.append(folder)
if what is not None:
sqlStatementParts.append("%s LIKE ?" % (where))
sqlArguments.append(what)
if unreadOnly:
sqlStatementParts.append("read = 0")
if len(sqlStatementParts) > 0:
sqlStatementBase += "WHERE " + " AND ".join(sqlStatementParts)
queryreturn = sqlQuery(sqlStatementBase, sqlArguments)
sqlStatement = ''' tableWidget.setRowCount(0)
SELECT msgid, toaddress, fromaddress, subject, received, read if account is not None:
FROM inbox WHERE folder="inbox" AND %s LIKE ? tableWidget.setColumnHidden(0, True)
ORDER BY received tableWidget.setColumnHidden(1, False)
''' % (where,) else:
tableWidget.setColumnHidden(0, False)
while self.ui.tableWidgetInbox.rowCount() > 0: tableWidget.setColumnHidden(1, False)
self.ui.tableWidgetInbox.removeRow(0) tableWidget.setSortingEnabled(False)
font = QFont()
font.setBold(True)
queryreturn = sqlQuery(sqlStatement, what)
for row in queryreturn: for row in queryreturn:
msgid, toAddress, fromAddress, subject, received, read = row msgfolder, msgid, toAddress, fromAddress, subject, received, read = row
subject = shared.fixPotentiallyInvalidUTF8Data(subject) self.addMessageListItemInbox(tableWidget, msgfolder, msgid, toAddress, fromAddress, subject, received, read)
try:
if toAddress == self.str_broadcast_subscribers:
toLabel = self.str_broadcast_subscribers
else:
toLabel = shared.config.get(toAddress, 'label')
except:
toLabel = ''
if toLabel == '':
toLabel = toAddress
fromLabel = '' tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder)
if shared.config.has_section(fromAddress): tableWidget.setSortingEnabled(True)
fromLabel = shared.config.get(fromAddress, 'label') tableWidget.keyPressEvent = self.tableWidgetInboxKeyPressEvent
if fromLabel == '': # If the fromAddress isn't one of our addresses and isn't a chan
queryreturn = sqlQuery(
'''select label from addressbook where address=?''', fromAddress)
if queryreturn != []:
for row in queryreturn:
fromLabel, = row
if fromLabel == '': # If this address wasn't in our address book...
queryreturn = sqlQuery(
'''select label from subscriptions where address=?''', fromAddress)
if queryreturn != []:
for row in queryreturn:
fromLabel, = row
if fromLabel == '':
fromLabel = fromAddress
# message row
self.ui.tableWidgetInbox.insertRow(0)
# to
to_item = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8'))
to_item.setToolTip(unicode(toLabel, 'utf-8'))
to_item.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not read:
to_item.setFont(font)
to_item.setData(Qt.UserRole, str(toAddress))
if shared.safeConfigGetBoolean(toAddress, 'mailinglist'):
to_item.setTextColor(QtGui.QColor(137, 04, 177)) # magenta
if shared.safeConfigGetBoolean(str(toAddress), 'chan'):
to_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange
to_item.setIcon(avatarize(toAddress))
self.ui.tableWidgetInbox.setItem(0, 0, to_item)
# from
from_item = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8'))
from_item.setToolTip(unicode(fromLabel, 'utf-8'))
from_item.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not read:
from_item.setFont(font)
from_item.setData(Qt.UserRole, str(fromAddress))
if shared.safeConfigGetBoolean(str(fromAddress), 'chan'):
from_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange
from_item.setIcon(avatarize(fromAddress))
self.ui.tableWidgetInbox.setItem(0, 1, from_item)
# subject
subject_item = QtGui.QTableWidgetItem(unicode(subject, 'utf-8'))
subject_item.setToolTip(unicode(subject, 'utf-8'))
subject_item.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not read:
subject_item.setFont(font)
self.ui.tableWidgetInbox.setItem(0, 2, subject_item)
# time received
time_item = myTableWidgetItem(l10n.formatTimestamp(received))
time_item.setToolTip(l10n.formatTimestamp(received))
time_item.setData(Qt.UserRole, QByteArray(msgid))
time_item.setData(33, int(received))
time_item.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not read:
time_item.setFont(font)
self.ui.tableWidgetInbox.setItem(0, 3, time_item)
self.ui.tableWidgetInbox.sortItems(3, Qt.DescendingOrder)
self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent
# create application indicator # create application indicator
def appIndicatorInit(self, app): def appIndicatorInit(self, app):
@ -1065,11 +1230,11 @@ class MyForm(QtGui.QMainWindow):
actionSubscribe.triggered.connect(self.appIndicatorSubscribe) actionSubscribe.triggered.connect(self.appIndicatorSubscribe)
m.addAction(actionSubscribe) m.addAction(actionSubscribe)
# Address book # Channels
actionAddressBook = QtGui.QAction(_translate( actionSubscribe = QtGui.QAction(_translate(
"MainWindow", "Address Book"), m, checkable=False) "MainWindow", "Channel"), m, checkable=False)
actionAddressBook.triggered.connect(self.appIndicatorAddressBook) actionSubscribe.triggered.connect(self.appIndicatorChannel)
m.addAction(actionAddressBook) m.addAction(actionSubscribe)
# separator # separator
actionSeparator = QtGui.QAction('', m, checkable=False) actionSeparator = QtGui.QAction('', m, checkable=False)
@ -1115,7 +1280,7 @@ class MyForm(QtGui.QMainWindow):
for row in queryreturn: for row in queryreturn:
toAddress, read = row toAddress, read = row
if not read: if not read:
if toAddress == self.str_broadcast_subscribers: if toAddress == str_broadcast_subscribers:
if self.mmapp.has_source("Subscriptions"): if self.mmapp.has_source("Subscriptions"):
self.mmapp.remove_source("Subscriptions") self.mmapp.remove_source("Subscriptions")
else: else:
@ -1133,8 +1298,8 @@ class MyForm(QtGui.QMainWindow):
msgid, toAddress, read = row msgid, toAddress, read = row
try: try:
if toAddress == self.str_broadcast_subscribers: if toAddress == str_broadcast_subscribers:
toLabel = self.str_broadcast_subscribers toLabel = str_broadcast_subscribers
else: else:
toLabel = shared.config.get(toAddress, 'label') toLabel = shared.config.get(toAddress, 'label')
except: except:
@ -1143,7 +1308,7 @@ class MyForm(QtGui.QMainWindow):
toLabel = toAddress toLabel = toAddress
if not read: if not read:
if toLabel == self.str_broadcast_subscribers: if toLabel == str_broadcast_subscribers:
# increment the unread subscriptions # increment the unread subscriptions
unreadSubscriptions = unreadSubscriptions + 1 unreadSubscriptions = unreadSubscriptions + 1
else: else:
@ -1179,7 +1344,7 @@ class MyForm(QtGui.QMainWindow):
# has messageing menu been installed # has messageing menu been installed
if not withMessagingMenu: if not withMessagingMenu:
print 'WARNING: MessagingMenu is not available. Is libmessaging-menu-dev installed?' logger.warning('WARNING: MessagingMenu is not available. Is libmessaging-menu-dev installed?')
return return
# create the menu server # create the menu server
@ -1192,7 +1357,7 @@ class MyForm(QtGui.QMainWindow):
self.ubuntuMessagingMenuUnread(True) self.ubuntuMessagingMenuUnread(True)
except Exception: except Exception:
withMessagingMenu = False withMessagingMenu = False
print 'WARNING: messaging menu disabled' logger.warning('WARNING: messaging menu disabled')
# update the Ubuntu messaging menu # update the Ubuntu messaging menu
def ubuntuMessagingMenuUpdate(self, drawAttention, newItem, toLabel): def ubuntuMessagingMenuUpdate(self, drawAttention, newItem, toLabel):
@ -1204,11 +1369,11 @@ class MyForm(QtGui.QMainWindow):
# has messageing menu been installed # has messageing menu been installed
if not withMessagingMenu: if not withMessagingMenu:
print 'WARNING: messaging menu disabled or libmessaging-menu-dev not installed' logger.warning('WARNING: messaging menu disabled or libmessaging-menu-dev not installed')
return return
# remember this item to that the messaging menu can find it # remember this item to that the messaging menu can find it
if toLabel == self.str_broadcast_subscribers: if toLabel == str_broadcast_subscribers:
self.newBroadcastItem = newItem self.newBroadcastItem = newItem
else: else:
self.newMessageItem = newItem self.newMessageItem = newItem
@ -1298,7 +1463,7 @@ class MyForm(QtGui.QMainWindow):
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
gst_available=True gst_available=True
except: except:
print "WARNING: gst123 must be installed in order to play mp3 sounds" logger.warning("WARNING: gst123 must be installed in order to play mp3 sounds")
if not gst_available: if not gst_available:
try: try:
subprocess.call(["mpg123", soundFilename], subprocess.call(["mpg123", soundFilename],
@ -1306,14 +1471,14 @@ class MyForm(QtGui.QMainWindow):
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
gst_available=True gst_available=True
except: except:
print "WARNING: mpg123 must be installed in order to play mp3 sounds" logger.warning("WARNING: mpg123 must be installed in order to play mp3 sounds")
else: else:
try: try:
subprocess.call(["aplay", soundFilename], subprocess.call(["aplay", soundFilename],
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
except: except:
print "WARNING: aplay must be installed in order to play WAV sounds" logger.warning("WARNING: aplay must be installed in order to play WAV sounds")
elif sys.platform[0:3] == 'win': elif sys.platform[0:3] == 'win':
# use winsound on Windows # use winsound on Windows
import winsound import winsound
@ -1346,16 +1511,19 @@ class MyForm(QtGui.QMainWindow):
else: else:
self.tray.showMessage(title, subtitle, 1, 2000) self.tray.showMessage(title, subtitle, 1, 2000)
# set delete key in inbox
def tableWidgetInboxKeyPressEvent(self, event): def tableWidgetInboxKeyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Delete: if event.key() == QtCore.Qt.Key_Delete:
self.on_action_InboxTrash() self.on_action_InboxTrash()
return QtGui.QTableWidget.keyPressEvent(self.ui.tableWidgetInbox, event) return QtGui.QTableWidget.keyPressEvent(self.getCurrentMessagelist(), event)
# set delete key in inbox
def tableWidgetSentKeyPressEvent(self, event): def tableWidgetSentKeyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Delete: if event.key() == QtCore.Qt.Key_Delete:
self.on_action_SentTrash() self.on_action_SentTrash()
return QtGui.QTableWidget.keyPressEvent(self.ui.tableWidgetSent, event) return QtGui.QTableWidget.keyPressEvent(self.getCurrentMessagelist(), event)
# menu button 'manage keys'
def click_actionManageKeys(self): def click_actionManageKeys(self):
if 'darwin' in sys.platform or 'linux' in sys.platform: if 'darwin' in sys.platform or 'linux' in sys.platform:
if shared.appdata == '': if shared.appdata == '':
@ -1379,11 +1547,23 @@ class MyForm(QtGui.QMainWindow):
if reply == QtGui.QMessageBox.Yes: if reply == QtGui.QMessageBox.Yes:
shared.openKeysFile() shared.openKeysFile()
# menu button 'delete all treshed messages'
def click_actionDeleteAllTrashedMessages(self): def click_actionDeleteAllTrashedMessages(self):
if QtGui.QMessageBox.question(self, _translate("MainWindow", "Delete trash?"), _translate("MainWindow", "Are you sure you want to delete all trashed messages?"), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) == QtGui.QMessageBox.No: if QtGui.QMessageBox.question(self, _translate("MainWindow", "Delete trash?"), _translate("MainWindow", "Are you sure you want to delete all trashed messages?"), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) == QtGui.QMessageBox.No:
return return
sqlStoredProcedure('deleteandvacuume') sqlStoredProcedure('deleteandvacuume')
self.rerenderTabTreeMessages()
self.rerenderTabTreeSubscriptions()
self.rerenderTabTreeChans()
if self.getCurrentFolder(self.ui.treeWidgetYourIdentities) == "trash":
self.loadMessagelist(self.ui.tableWidgetInbox, self.getCurrentAccount(self.ui.treeWidgetYourIdentities), "trash")
elif self.getCurrentFolder(self.ui.treeWidgetSubscriptions) == "trash":
self.loadMessagelist(self.ui.tableWidgetInboxSubscriptions, self.getCurrentAccount(self.ui.treeWidgetSubscriptions), "trash")
elif self.getCurrentFolder(self.ui.treeWidgetChans) == "trash":
self.loadMessagelist(self.ui.tableWidgetInboxChans, self.getCurrentAccount(self.ui.treeWidgetChans), "trash")
# menu botton 'regenerate deterministic addresses'
def click_actionRegenerateDeterministicAddresses(self): def click_actionRegenerateDeterministicAddresses(self):
self.regenerateAddressesDialogInstance = regenerateAddressesDialog( self.regenerateAddressesDialogInstance = regenerateAddressesDialog(
self) self)
@ -1409,6 +1589,7 @@ class MyForm(QtGui.QMainWindow):
), self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text().toUtf8(), self.regenerateAddressesDialogInstance.ui.checkBoxEighteenByteRipe.isChecked())) ), self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text().toUtf8(), self.regenerateAddressesDialogInstance.ui.checkBoxEighteenByteRipe.isChecked()))
self.ui.tabWidget.setCurrentIndex(3) self.ui.tabWidget.setCurrentIndex(3)
# opens 'join chan' dialog
def click_actionJoinChan(self): def click_actionJoinChan(self):
self.newChanDialogInstance = newChanDialog(self) self.newChanDialogInstance = newChanDialog(self)
if self.newChanDialogInstance.exec_(): if self.newChanDialogInstance.exec_():
@ -1420,7 +1601,7 @@ class MyForm(QtGui.QMainWindow):
shared.apiAddressGeneratorReturnQueue.queue.clear() shared.apiAddressGeneratorReturnQueue.queue.clear()
shared.addressGeneratorQueue.put(('createChan', 4, 1, self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8())) shared.addressGeneratorQueue.put(('createChan', 4, 1, self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8()))
addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get()
print 'addressGeneratorReturnValue', addressGeneratorReturnValue logger.debug('addressGeneratorReturnValue ' + str(addressGeneratorReturnValue))
if len(addressGeneratorReturnValue) == 0: if len(addressGeneratorReturnValue) == 0:
QMessageBox.about(self, _translate("MainWindow", "Address already present"), _translate( QMessageBox.about(self, _translate("MainWindow", "Address already present"), _translate(
"MainWindow", "Could not add chan because it appears to already be one of your identities.")) "MainWindow", "Could not add chan because it appears to already be one of your identities."))
@ -1445,7 +1626,7 @@ class MyForm(QtGui.QMainWindow):
shared.apiAddressGeneratorReturnQueue.queue.clear() shared.apiAddressGeneratorReturnQueue.queue.clear()
shared.addressGeneratorQueue.put(('joinChan', addBMIfNotPresent(self.newChanDialogInstance.ui.lineEditChanBitmessageAddress.text()), self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameJoin.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameJoin.text().toUtf8())) shared.addressGeneratorQueue.put(('joinChan', addBMIfNotPresent(self.newChanDialogInstance.ui.lineEditChanBitmessageAddress.text()), self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameJoin.text().toUtf8()), self.newChanDialogInstance.ui.lineEditChanNameJoin.text().toUtf8()))
addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get()
print 'addressGeneratorReturnValue', addressGeneratorReturnValue logger.debug('addressGeneratorReturnValue ' + str(addressGeneratorReturnValue))
if addressGeneratorReturnValue == 'chan name does not match address': if addressGeneratorReturnValue == 'chan name does not match address':
QMessageBox.about(self, _translate("MainWindow", "Address does not match chan name"), _translate( QMessageBox.about(self, _translate("MainWindow", "Address does not match chan name"), _translate(
"MainWindow", "Although the Bitmessage address you entered was valid, it doesn\'t match the chan name.")) "MainWindow", "Although the Bitmessage address you entered was valid, it doesn\'t match the chan name."))
@ -1458,6 +1639,7 @@ class MyForm(QtGui.QMainWindow):
QMessageBox.about(self, _translate("MainWindow", "Success"), _translate( QMessageBox.about(self, _translate("MainWindow", "Success"), _translate(
"MainWindow", "Successfully joined chan. ")) "MainWindow", "Successfully joined chan. "))
self.ui.tabWidget.setCurrentIndex(3) self.ui.tabWidget.setCurrentIndex(3)
self.rerenderAddressBook()
def showConnectDialog(self): def showConnectDialog(self):
self.connectDialogInstance = connectDialog(self) self.connectDialogInstance = connectDialog(self)
@ -1468,11 +1650,18 @@ class MyForm(QtGui.QMainWindow):
else: else:
self.click_actionSettings() self.click_actionSettings()
def showMigrationWizard(self, level):
self.migrationWizardInstance = Ui_MigrationWizard(["a"])
if self.migrationWizardInstance.exec_():
pass
else:
pass
def changeEvent(self, event): def changeEvent(self, event):
if event.type() == QtCore.QEvent.LanguageChange: if event.type() == QtCore.QEvent.LanguageChange:
self.ui.retranslateUi(self) self.ui.retranslateUi(self)
self.init_inbox_popup_menu(False) self.init_inbox_popup_menu(False)
self.init_identities_popup_menu(False) self.init_chan_popup_menu(False)
self.init_addressbook_popup_menu(False) self.init_addressbook_popup_menu(False)
self.init_subscriptions_popup_menu(False) self.init_subscriptions_popup_menu(False)
self.init_sent_popup_menu(False) self.init_sent_popup_menu(False)
@ -1480,9 +1669,7 @@ class MyForm(QtGui.QMainWindow):
if event.type() == QtCore.QEvent.WindowStateChange: if event.type() == QtCore.QEvent.WindowStateChange:
if self.windowState() & QtCore.Qt.WindowMinimized: if self.windowState() & QtCore.Qt.WindowMinimized:
if shared.config.getboolean('bitmessagesettings', 'minimizetotray') and not 'darwin' in sys.platform: if shared.config.getboolean('bitmessagesettings', 'minimizetotray') and not 'darwin' in sys.platform:
self.appIndicatorHide() QTimer.singleShot(0, self.appIndicatorHide)
if 'win32' in sys.platform or 'win64' in sys.platform:
self.setWindowFlags(Qt.ToolTip)
elif event.oldState() & QtCore.Qt.WindowMinimized: elif event.oldState() & QtCore.Qt.WindowMinimized:
# The window state has just been changed to # The window state has just been changed to
# Normal/Maximised/FullScreen # Normal/Maximised/FullScreen
@ -1496,14 +1683,17 @@ class MyForm(QtGui.QMainWindow):
self.appIndicatorShowOrHideWindow() self.appIndicatorShowOrHideWindow()
def updateNumberOfMessagesProcessed(self): def updateNumberOfMessagesProcessed(self):
self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer.itervalues()))))
self.ui.labelMessageCount.setText(_translate( self.ui.labelMessageCount.setText(_translate(
"MainWindow", "Processed %1 person-to-person messages.").arg(str(shared.numberOfMessagesProcessed))) "MainWindow", "Processed %1 person-to-person messages.").arg(str(shared.numberOfMessagesProcessed)))
def updateNumberOfBroadcastsProcessed(self): def updateNumberOfBroadcastsProcessed(self):
self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer.itervalues()))))
self.ui.labelBroadcastCount.setText(_translate( self.ui.labelBroadcastCount.setText(_translate(
"MainWindow", "Processed %1 broadcast messages.").arg(str(shared.numberOfBroadcastsProcessed))) "MainWindow", "Processed %1 broadcast messages.").arg(str(shared.numberOfBroadcastsProcessed)))
def updateNumberOfPubkeysProcessed(self): def updateNumberOfPubkeysProcessed(self):
self.ui.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced: %1").arg(str(sum(shared.numberOfObjectsThatWeHaveYetToGetPerPeer.itervalues()))))
self.ui.labelPubkeyCount.setText(_translate( self.ui.labelPubkeyCount.setText(_translate(
"MainWindow", "Processed %1 public keys.").arg(str(shared.numberOfPubkeysProcessed))) "MainWindow", "Processed %1 public keys.").arg(str(shared.numberOfPubkeysProcessed)))
@ -1533,7 +1723,6 @@ class MyForm(QtGui.QMainWindow):
shared.numberOfBytesSent = 0 shared.numberOfBytesSent = 0
def updateNetworkStatusTab(self): def updateNetworkStatusTab(self):
# print 'updating network status tab'
totalNumberOfConnectionsFromAllStreams = 0 # One would think we could use len(sendDataQueues) for this but the number doesn't always match: just because we have a sendDataThread running doesn't mean that the connection has been fully established (with the exchange of version messages). totalNumberOfConnectionsFromAllStreams = 0 # One would think we could use len(sendDataQueues) for this but the number doesn't always match: just because we have a sendDataThread running doesn't mean that the connection has been fully established (with the exchange of version messages).
streamNumberTotals = {} streamNumberTotals = {}
for host, streamNumber in shared.connectedHostsList.items(): for host, streamNumber in shared.connectedHostsList.items():
@ -1684,60 +1873,107 @@ class MyForm(QtGui.QMainWindow):
def drawTrayIcon(self, iconFileName, inboxUnreadCount): def drawTrayIcon(self, iconFileName, inboxUnreadCount):
self.tray.setIcon(self.calcTrayIcon(iconFileName, inboxUnreadCount)) self.tray.setIcon(self.calcTrayIcon(iconFileName, inboxUnreadCount))
def changedInboxUnread(self): def changedInboxUnread(self, row = None):
self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount()) self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount())
self.rerenderTabTreeMessages()
self.rerenderTabTreeSubscriptions()
self.rerenderTabTreeChans()
def findInboxUnreadCount(self): def findInboxUnreadCount(self, count = None):
queryreturn = sqlQuery('''SELECT count(*) from inbox WHERE folder='inbox' and read=0''') if count is None:
cnt = 0 queryreturn = sqlQuery('''SELECT count(*) from inbox WHERE folder='inbox' and read=0''')
for row in queryreturn: cnt = 0
cnt, = row for row in queryreturn:
return int(cnt) cnt, = row
self.unreadCount = int(cnt)
else:
self.unreadCount = count
return self.unreadCount
def updateSentItemStatusByToAddress(self, toAddress, textToDisplay): def updateSentItemStatusByToAddress(self, toAddress, textToDisplay):
for i in range(self.ui.tableWidgetSent.rowCount()): for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]:
rowAddress = str(self.ui.tableWidgetSent.item( treeWidget = self.widgetConvert(sent)
i, 0).data(Qt.UserRole).toPyObject()) if self.getCurrentFolder(treeWidget) != "sent":
if toAddress == rowAddress: continue
self.ui.tableWidgetSent.item(i, 3).setToolTip(textToDisplay) if treeWidget in [self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] and self.getCurrentAccount(treeWidget) != toAddress:
try: continue
newlinePosition = textToDisplay.indexOf('\n')
except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception. for i in range(sent.rowCount()):
newlinePosition = 0 rowAddress = str(sent.item(
if newlinePosition > 1: i, 0).data(Qt.UserRole).toPyObject())
self.ui.tableWidgetSent.item(i, 3).setText( if toAddress == rowAddress:
textToDisplay[:newlinePosition]) sent.item(i, 3).setToolTip(textToDisplay)
else: try:
self.ui.tableWidgetSent.item(i, 3).setText(textToDisplay) newlinePosition = textToDisplay.indexOf('\n')
except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception.
newlinePosition = 0
if newlinePosition > 1:
sent.item(i, 3).setText(
textToDisplay[:newlinePosition])
else:
sent.item(i, 3).setText(textToDisplay)
def updateSentItemStatusByAckdata(self, ackdata, textToDisplay): def updateSentItemStatusByAckdata(self, ackdata, textToDisplay):
for i in range(self.ui.tableWidgetSent.rowCount()): for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]:
toAddress = str(self.ui.tableWidgetSent.item( treeWidget = self.widgetConvert(sent)
i, 0).data(Qt.UserRole).toPyObject()) if self.getCurrentFolder(treeWidget) != "sent":
tableAckdata = self.ui.tableWidgetSent.item( continue
i, 3).data(Qt.UserRole).toPyObject() for i in range(sent.rowCount()):
status, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress = str(sent.item(
toAddress) i, 0).data(Qt.UserRole).toPyObject())
if ackdata == tableAckdata: tableAckdata = sent.item(
self.ui.tableWidgetSent.item(i, 3).setToolTip(textToDisplay) i, 3).data(Qt.UserRole).toPyObject()
try: status, addressVersionNumber, streamNumber, ripe = decodeAddress(
newlinePosition = textToDisplay.indexOf('\n') toAddress)
except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception. if ackdata == tableAckdata:
newlinePosition = 0 sent.item(i, 3).setToolTip(textToDisplay)
if newlinePosition > 1: try:
self.ui.tableWidgetSent.item(i, 3).setText( newlinePosition = textToDisplay.indexOf('\n')
textToDisplay[:newlinePosition]) except: # If someone misses adding a "_translate" to a string before passing it to this function, this function won't receive a qstring which will cause an exception.
else: newlinePosition = 0
self.ui.tableWidgetSent.item(i, 3).setText(textToDisplay) if newlinePosition > 1:
sent.item(i, 3).setText(
textToDisplay[:newlinePosition])
else:
sent.item(i, 3).setText(textToDisplay)
def removeInboxRowByMsgid(self, msgid): # msgid and inventoryHash are the same thing def removeInboxRowByMsgid(self, msgid): # msgid and inventoryHash are the same thing
for i in range(self.ui.tableWidgetInbox.rowCount()): for inbox in ([
if msgid == str(self.ui.tableWidgetInbox.item(i, 3).data(Qt.UserRole).toPyObject()): self.ui.tableWidgetInbox,
self.statusBar().showMessage(_translate( self.ui.tableWidgetInboxSubscriptions,
"MainWindow", "Message trashed")) self.ui.tableWidgetInboxChans]):
self.ui.tableWidgetInbox.removeRow(i) for i in range(inbox.rowCount()):
break if msgid == str(inbox.item(i, 3).data(Qt.UserRole).toPyObject()):
self.changedInboxUnread() self.statusBar().showMessage(_translate(
"MainWindow", "Message trashed"))
treeWidget = self.widgetConvert(inbox)
self.propagateUnreadCount(str(inbox.item(i, 1 if inbox == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), self.getCurrentFolder(treeWidget), treeWidget, 0)
inbox.removeRow(i)
break
def newVersionAvailable(self, version):
# if (not (self.windowState() & QtCore.Qt.WindowActive)) or (self.windowState() & QtCore.Qt.WindowMinimized):
# return
# only notify once until next restart
try:
if self.notifiedNewVersion:
return
except AttributeError:
pass
self.notifiedNewVersion = ".".join(str(n) for n in version)
message = "New "
if version[1] % 2:
message += "UNSTABLE"
else:
message += "stable"
message += " version of PyBitmessage is available: " + self.notifiedNewVersion + ". Download it from https://github.com/"
if version[0] == 0 and version[1] == 5:
message += "mailchuck"
else:
message += "Bitmessage"
message += "/PyBitmessage/releases/latest"
self.displayAlert("New release of PyBitmessage available", message, False)
def displayAlert(self, title, text, exitAfterUserClicksOk): def displayAlert(self, title, text, exitAfterUserClicksOk):
self.statusBar().showMessage(text) self.statusBar().showMessage(text)
@ -1809,22 +2045,22 @@ class MyForm(QtGui.QMainWindow):
i, 0).setTextColor(QApplication.palette().text().color()) i, 0).setTextColor(QApplication.palette().text().color())
def rerenderSentFromLabels(self): def rerenderSentFromLabels(self):
for i in range(self.ui.tableWidgetSent.rowCount()): for i in range(self.ui.tableWidgetInbox.rowCount()):
fromAddress = str(self.ui.tableWidgetSent.item( fromAddress = str(self.ui.tableWidgetInbox.item(
i, 1).data(Qt.UserRole).toPyObject()) i, 1).data(Qt.UserRole).toPyObject())
# Message might be from an address we own like a chan address. Let's look for that label. # Message might be from an address we own like a chan address. Let's look for that label.
if shared.config.has_section(fromAddress): if shared.config.has_section(fromAddress):
fromLabel = shared.config.get(fromAddress, 'label') fromLabel = shared.config.get(fromAddress, 'label')
else: else:
fromLabel = fromAddress fromLabel = fromAddress
self.ui.tableWidgetSent.item( self.ui.tableWidgetInbox.item(
i, 1).setText(unicode(fromLabel, 'utf-8')) i, 1).setText(unicode(fromLabel, 'utf-8'))
self.ui.tableWidgetSent.item( self.ui.tableWidgetInbox.item(
i, 1).setIcon(avatarize(fromAddress)) i, 1).setIcon(avatarize(fromAddress))
def rerenderSentToLabels(self): def rerenderSentToLabels(self):
for i in range(self.ui.tableWidgetSent.rowCount()): for i in range(self.ui.tableWidgetInbox.rowCount()):
addressToLookup = str(self.ui.tableWidgetSent.item( addressToLookup = str(self.ui.tableWidgetInbox.item(
i, 0).data(Qt.UserRole).toPyObject()) i, 0).data(Qt.UserRole).toPyObject())
toLabel = '' toLabel = ''
queryreturn = sqlQuery( queryreturn = sqlQuery(
@ -1839,40 +2075,46 @@ class MyForm(QtGui.QMainWindow):
toLabel = shared.config.get(addressToLookup, 'label') toLabel = shared.config.get(addressToLookup, 'label')
if toLabel == '': if toLabel == '':
toLabel = addressToLookup toLabel = addressToLookup
self.ui.tableWidgetSent.item( self.ui.tableWidgetInbox.item(
i, 0).setText(unicode(toLabel, 'utf-8')) i, 0).setText(unicode(toLabel, 'utf-8'))
def rerenderAddressBook(self): def rerenderAddressBook(self):
def addRow (address, label, type):
self.ui.tableWidgetAddressBook.insertRow(0)
newItem = Ui_AddressBookWidgetItemLabel(address, unicode(label, 'utf-8'), type)
newItem.setData(Qt.UserRole, type)
self.ui.tableWidgetAddressBook.setItem(0, 0, newItem)
newItem = Ui_AddressBookWidgetItemAddress(address, unicode(label, 'utf-8'), type)
self.ui.tableWidgetAddressBook.setItem(0, 1, newItem)
self.ui.tableWidgetAddressBook.setSortingEnabled(False)
self.ui.tableWidgetAddressBook.setRowCount(0) self.ui.tableWidgetAddressBook.setRowCount(0)
# subscriptions
queryreturn = sqlQuery('SELECT label, address FROM subscriptions WHERE enabled = 1')
for row in queryreturn:
label, address = row
addRow(address, label, AccountMixin.SUBSCRIPTION)
# chans
addresses = getSortedAccounts()
for address in addresses:
account = accountClass(address)
if (account.type == AccountMixin.CHAN and shared.safeConfigGetBoolean(address, 'enabled')):
addRow(address, account.getLabel(), AccountMixin.CHAN)
# normal accounts
queryreturn = sqlQuery('SELECT * FROM addressbook') queryreturn = sqlQuery('SELECT * FROM addressbook')
for row in queryreturn: for row in queryreturn:
label, address = row label, address = row
self.ui.tableWidgetAddressBook.insertRow(0) addRow(address, label, AccountMixin.NORMAL)
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
newItem.setIcon(avatarize(address)) # sort
self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) self.ui.tableWidgetAddressBook.sortItems(0, QtCore.Qt.AscendingOrder)
newItem = QtGui.QTableWidgetItem(address) self.ui.tableWidgetAddressBook.setSortingEnabled(True)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.ui.tableWidgetAddressBook.setItem(0, 1, newItem)
def rerenderSubscriptions(self): def rerenderSubscriptions(self):
self.ui.tableWidgetSubscriptions.setRowCount(0) self.rerenderTabTreeSubscriptions()
queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions')
for row in queryreturn:
label, address, enabled = row
self.ui.tableWidgetSubscriptions.insertRow(0)
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
if not enabled:
newItem.setTextColor(QtGui.QColor(128, 128, 128))
newItem.setIcon(avatarize(address))
self.ui.tableWidgetSubscriptions.setItem(0, 0, newItem)
newItem = QtGui.QTableWidgetItem(address)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not enabled:
newItem.setTextColor(QtGui.QColor(128, 128, 128))
self.ui.tableWidgetSubscriptions.setItem(0, 1, newItem)
def rerenderBlackWhiteList(self): def rerenderBlackWhiteList(self):
self.ui.tableWidgetBlacklist.setRowCount(0) self.ui.tableWidgetBlacklist.setRowCount(0)
@ -1898,18 +2140,33 @@ class MyForm(QtGui.QMainWindow):
def click_pushButtonTTL(self): def click_pushButtonTTL(self):
QtGui.QMessageBox.information(self, 'Time To Live', _translate( QtGui.QMessageBox.information(self, 'Time To Live', _translate(
"MainWindow", "The TTL, or Time-To-Live is the length of time that the network will hold the message. \ "MainWindow", """The TTL, or Time-To-Live is the length of time that the network will hold the message.
The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it \ The recipient must get it during this time. If your Bitmessage client does not hear an acknowledgement, it
will resend the message automatically. The longer the Time-To-Live, the \ will resend the message automatically. The longer the Time-To-Live, the
more work your computer must do to send the message. A Time-To-Live of four or five days is often appropriate."), QMessageBox.Ok) more work your computer must do to send the message. A Time-To-Live of four or five days is often appropriate."""), QMessageBox.Ok)
def click_pushButtonSend(self): def click_pushButtonSend(self):
self.statusBar().showMessage('') self.statusBar().showMessage('')
toAddresses = str(self.ui.lineEditTo.text())
fromAddress = str(self.ui.labelFrom.text()) if self.ui.tabWidgetSend.currentIndex() == 0:
subject = str(self.ui.lineEditSubject.text().toUtf8()) # message to specific people
message = str( sendMessageToPeople = True
self.ui.textEditMessage.document().toPlainText().toUtf8()) fromAddress = str(self.ui.comboBoxSendFrom.itemData(
self.ui.comboBoxSendFrom.currentIndex(),
Qt.UserRole).toString())
toAddresses = str(self.ui.lineEditTo.text())
subject = str(self.ui.lineEditSubject.text().toUtf8())
message = str(
self.ui.textEditMessage.document().toPlainText().toUtf8())
else:
# broadcast message
sendMessageToPeople = False
fromAddress = str(self.ui.comboBoxSendFromBroadcast.itemData(
self.ui.comboBoxSendFromBroadcast.currentIndex(),
Qt.UserRole).toString())
subject = str(self.ui.lineEditSubjectBroadcast.text().toUtf8())
message = str(
self.ui.textEditMessageBroadcast.document().toPlainText().toUtf8())
""" """
The whole network message must fit in 2^18 bytes. Let's assume 500 The whole network message must fit in 2^18 bytes. Let's assume 500
bytes of overhead. If someone wants to get that too an exact bytes of overhead. If someone wants to get that too an exact
@ -1921,18 +2178,38 @@ more work your computer must do to send the message. A Time-To-Live of four or f
QMessageBox.about(self, _translate("MainWindow", "Message too long"), _translate( QMessageBox.about(self, _translate("MainWindow", "Message too long"), _translate(
"MainWindow", "The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending.").arg(len(message) - (2 ** 18 - 500))) "MainWindow", "The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending.").arg(len(message) - (2 ** 18 - 500)))
return return
if self.ui.radioButtonSpecific.isChecked(): # To send a message to specific people (rather than broadcast)
acct = accountClass(fromAddress)
if sendMessageToPeople: # To send a message to specific people (rather than broadcast)
toAddressesList = [s.strip() toAddressesList = [s.strip()
for s in toAddresses.replace(',', ';').split(';')] for s in toAddresses.replace(',', ';').split(';')]
toAddressesList = list(set( toAddressesList = list(set(
toAddressesList)) # remove duplicate addresses. If the user has one address with a BM- and the same address without the BM-, this will not catch it. They'll send the message to the person twice. toAddressesList)) # remove duplicate addresses. If the user has one address with a BM- and the same address without the BM-, this will not catch it. They'll send the message to the person twice.
for toAddress in toAddressesList: for toAddress in toAddressesList:
if toAddress != '': if toAddress != '':
if toAddress.find("@") >= 0:
if isinstance(acct, GatewayAccount):
acct.createMessage(toAddress, fromAddress, subject, message)
subject = acct.subject
toAddress = acct.toAddress
else:
email = acct.getLabel()
if email[-14:] != "@mailchuck.com": #attempt register
# 12 character random email address
email = ''.join(random.SystemRandom().choice(string.ascii_lowercase) for _ in range(12)) + "@mailchuck.com"
acct = MailchuckAccount(fromAddress)
acct.register(email)
shared.config.set(fromAddress, 'label', email)
shared.config.set(fromAddress, 'gateway', 'mailchuck')
shared.writeKeysFile()
self.statusBar().showMessage(_translate(
"MainWindow", "Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending.").arg(email))
return
status, addressVersionNumber, streamNumber, ripe = decodeAddress( status, addressVersionNumber, streamNumber, ripe = decodeAddress(
toAddress) toAddress)
if status != 'success': if status != 'success':
with shared.printLock: logger.error('Error: Could not decode ' + toAddress + ':' + status)
print 'Error: Could not decode', toAddress, ':', status
if status == 'missingbm': if status == 'missingbm':
self.statusBar().showMessage(_translate( self.statusBar().showMessage(_translate(
@ -1963,6 +2240,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f
"MainWindow", "Error: You must specify a From address. If you don\'t have one, go to the \'Your Identities\' tab.")) "MainWindow", "Error: You must specify a From address. If you don\'t have one, go to the \'Your Identities\' tab."))
else: else:
toAddress = addBMIfNotPresent(toAddress) toAddress = addBMIfNotPresent(toAddress)
if addressVersionNumber > 4 or addressVersionNumber <= 1: if addressVersionNumber > 4 or addressVersionNumber <= 1:
QMessageBox.about(self, _translate("MainWindow", "Address version number"), _translate( QMessageBox.about(self, _translate("MainWindow", "Address version number"), _translate(
"MainWindow", "Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(addressVersionNumber))) "MainWindow", "Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(addressVersionNumber)))
@ -2008,12 +2286,15 @@ more work your computer must do to send the message. A Time-To-Live of four or f
shared.workerQueue.put(('sendmessage', toAddress)) shared.workerQueue.put(('sendmessage', toAddress))
self.ui.comboBoxSendFrom.setCurrentIndex(0) self.ui.comboBoxSendFrom.setCurrentIndex(0)
self.ui.labelFrom.setText('')
self.ui.lineEditTo.setText('') self.ui.lineEditTo.setText('')
self.ui.lineEditSubject.setText('') self.ui.lineEditSubject.setText('')
self.ui.textEditMessage.setText('') self.ui.textEditMessage.setText('')
self.ui.tabWidget.setCurrentIndex(2) if self.replyFromTab is not None:
self.ui.tableWidgetSent.setCurrentCell(0, 0) self.ui.tabWidget.setCurrentIndex(self.replyFromTab)
self.replyFromTab = None
self.statusBar().showMessage(_translate(
"MainWindow", "Message queued."))
#self.ui.tableWidgetInbox.setCurrentCell(0, 0)
else: else:
self.statusBar().showMessage(_translate( self.statusBar().showMessage(_translate(
"MainWindow", "Your \'To\' field is empty.")) "MainWindow", "Your \'To\' field is empty."))
@ -2027,7 +2308,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f
# this is a broadcast message, but we can use it to update the # this is a broadcast message, but we can use it to update the
# user interface when the POW is done generating. # user interface when the POW is done generating.
ackdata = OpenSSL.rand(32) ackdata = OpenSSL.rand(32)
toAddress = self.str_broadcast_subscribers toAddress = str_broadcast_subscribers
ripe = '' ripe = ''
t = ('', # msgid. We don't know what this will be until the POW is done. t = ('', # msgid. We don't know what this will be until the POW is done.
toAddress, toAddress,
@ -2048,20 +2329,20 @@ more work your computer must do to send the message. A Time-To-Live of four or f
sqlExecute( sqlExecute(
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', *t) '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', *t)
toLabel = self.str_broadcast_subscribers toLabel = str_broadcast_subscribers
self.displayNewSentMessage( self.displayNewSentMessage(
toAddress, toLabel, fromAddress, subject, message, ackdata) toAddress, toLabel, fromAddress, subject, message, ackdata)
shared.workerQueue.put(('sendbroadcast', '')) shared.workerQueue.put(('sendbroadcast', ''))
self.ui.comboBoxSendFrom.setCurrentIndex(0) self.ui.comboBoxSendFromBroadcast.setCurrentIndex(0)
self.ui.labelFrom.setText('') self.ui.lineEditSubjectBroadcast.setText('')
self.ui.lineEditTo.setText('') self.ui.textEditMessageBroadcast.setText('')
self.ui.lineEditSubject.setText('') self.ui.tabWidget.setCurrentIndex(1)
self.ui.textEditMessage.setText('') self.ui.tableWidgetInboxSubscriptions.setCurrentCell(0, 0)
self.ui.tabWidget.setCurrentIndex(2) self.statusBar().showMessage(_translate(
self.ui.tableWidgetSent.setCurrentCell(0, 0) "MainWindow", "Broadcast queued."))
def click_pushButtonLoadFromAddressBook(self): def click_pushButtonLoadFromAddressBook(self):
self.ui.tabWidget.setCurrentIndex(5) self.ui.tabWidget.setCurrentIndex(5)
@ -2083,38 +2364,51 @@ more work your computer must do to send the message. A Time-To-Live of four or f
self.statusBar().showMessage(_translate( self.statusBar().showMessage(_translate(
"MainWindow", "Fetched address from namecoin identity.")) "MainWindow", "Fetched address from namecoin identity."))
def redrawLabelFrom(self, index): def setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(self, address):
self.ui.labelFrom.setText(
self.ui.comboBoxSendFrom.itemData(index).toPyObject())
self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(self.ui.comboBoxSendFrom.itemData(index).toPyObject())
def setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(self, address):
# If this is a chan then don't let people broadcast because no one # If this is a chan then don't let people broadcast because no one
# should subscribe to chan addresses. # should subscribe to chan addresses.
if shared.safeConfigGetBoolean(str(address), 'chan'): if shared.safeConfigGetBoolean(str(address), 'mailinglist'):
self.ui.radioButtonSpecific.click() self.ui.tabWidgetSend.setCurrentIndex(1)
self.ui.radioButtonBroadcast.setEnabled(False)
else: else:
self.ui.radioButtonBroadcast.setEnabled(True) self.ui.tabWidgetSend.setCurrentIndex(0)
def rerenderComboBoxSendFrom(self): def rerenderComboBoxSendFrom(self):
self.ui.comboBoxSendFrom.clear() self.ui.comboBoxSendFrom.clear()
self.ui.labelFrom.setText('') for addressInKeysFile in getSortedAccounts():
configSections = shared.config.sections() isEnabled = shared.config.getboolean(
for addressInKeysFile in configSections: addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read.
if addressInKeysFile != 'bitmessagesettings': isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist')
isEnabled = shared.config.getboolean( if isEnabled and not isMaillinglist:
addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. self.ui.comboBoxSendFrom.addItem(avatarize(addressInKeysFile), unicode(shared.config.get(
if isEnabled: addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile)
self.ui.comboBoxSendFrom.insertItem(0, avatarize(addressInKeysFile), unicode(shared.config.get( # self.ui.comboBoxSendFrom.model().sort(1, Qt.AscendingOrder)
addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) for i in range(self.ui.comboBoxSendFrom.count()):
address = str(self.ui.comboBoxSendFrom.itemData(i, Qt.UserRole).toString())
self.ui.comboBoxSendFrom.setItemData(i, AccountColor(address).accountColor(), Qt.ForegroundRole)
self.ui.comboBoxSendFrom.insertItem(0, '', '') self.ui.comboBoxSendFrom.insertItem(0, '', '')
if(self.ui.comboBoxSendFrom.count() == 2): if(self.ui.comboBoxSendFrom.count() == 2):
self.ui.comboBoxSendFrom.setCurrentIndex(1) self.ui.comboBoxSendFrom.setCurrentIndex(1)
self.redrawLabelFrom(self.ui.comboBoxSendFrom.currentIndex())
else: else:
self.ui.comboBoxSendFrom.setCurrentIndex(0) self.ui.comboBoxSendFrom.setCurrentIndex(0)
def rerenderComboBoxSendFromBroadcast(self):
self.ui.comboBoxSendFromBroadcast.clear()
for addressInKeysFile in getSortedAccounts():
isEnabled = shared.config.getboolean(
addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read.
isChan = shared.safeConfigGetBoolean(addressInKeysFile, 'chan')
if isEnabled and not isChan:
self.ui.comboBoxSendFromBroadcast.addItem(avatarize(addressInKeysFile), unicode(shared.config.get(
addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile)
for i in range(self.ui.comboBoxSendFromBroadcast.count()):
address = str(self.ui.comboBoxSendFromBroadcast.itemData(i, Qt.UserRole).toString())
self.ui.comboBoxSendFromBroadcast.setItemData(i, AccountColor(address).accountColor(), Qt.ForegroundRole)
self.ui.comboBoxSendFromBroadcast.insertItem(0, '', '')
if(self.ui.comboBoxSendFromBroadcast.count() == 2):
self.ui.comboBoxSendFromBroadcast.setCurrentIndex(1)
else:
self.ui.comboBoxSendFromBroadcast.setCurrentIndex(0)
# This function is called by the processmsg function when that function # This function is called by the processmsg function when that function
# receives a message to an address that is acting as a # receives a message to an address that is acting as a
# pseudo-mailing-list. The message will be broadcast out. This function # pseudo-mailing-list. The message will be broadcast out. This function
@ -2122,116 +2416,45 @@ more work your computer must do to send the message. A Time-To-Live of four or f
def displayNewSentMessage(self, toAddress, toLabel, fromAddress, subject, message, ackdata): def displayNewSentMessage(self, toAddress, toLabel, fromAddress, subject, message, ackdata):
subject = shared.fixPotentiallyInvalidUTF8Data(subject) subject = shared.fixPotentiallyInvalidUTF8Data(subject)
message = shared.fixPotentiallyInvalidUTF8Data(message) message = shared.fixPotentiallyInvalidUTF8Data(message)
try: acct = accountClass(fromAddress)
fromLabel = shared.config.get(fromAddress, 'label') acct.parseMessage(toAddress, fromAddress, subject, message)
except: for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]:
fromLabel = '' treeWidget = self.widgetConvert(sent)
if fromLabel == '': if self.getCurrentFolder(treeWidget) != "sent":
fromLabel = fromAddress continue
if treeWidget == self.ui.treeWidgetYourIdentities and self.getCurrentAccount(treeWidget) != fromAddress:
self.ui.tableWidgetSent.setSortingEnabled(False) continue
self.ui.tableWidgetSent.insertRow(0) elif treeWidget in [self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] and self.getCurrentAccount(treeWidget) != toAddress:
if toLabel == '': continue
newItem = QtGui.QTableWidgetItem(unicode(toAddress, 'utf-8'))
newItem.setToolTip(unicode(toAddress, 'utf-8')) self.addMessageListItemSent(sent, toAddress, fromAddress, subject, "msgqueued", ackdata, time.time())
else: self.getAccountTextedit(acct).setPlainText(unicode(message, 'utf-8)'))
newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8'))
newItem.setToolTip(unicode(toLabel, 'utf-8'))
newItem.setData(Qt.UserRole, str(toAddress))
newItem.setIcon(avatarize(toAddress))
self.ui.tableWidgetSent.setItem(0, 0, newItem)
if fromLabel == '':
newItem = QtGui.QTableWidgetItem(unicode(fromAddress, 'utf-8'))
newItem.setToolTip(unicode(fromAddress, 'utf-8'))
else:
newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8'))
newItem.setToolTip(unicode(fromLabel, 'utf-8'))
newItem.setData(Qt.UserRole, str(fromAddress))
newItem.setIcon(avatarize(fromAddress))
self.ui.tableWidgetSent.setItem(0, 1, newItem)
newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)'))
newItem.setToolTip(unicode(subject, 'utf-8)'))
#newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed.
self.ui.tableWidgetSent.setItem(0, 2, newItem)
# newItem = QtGui.QTableWidgetItem('Doing work necessary to send
# broadcast...'+
# l10n.formatTimestamp())
newItem = myTableWidgetItem(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp()))
newItem.setToolTip(_translate("MainWindow", "Work is queued. %1").arg(l10n.formatTimestamp()))
newItem.setData(Qt.UserRole, QByteArray(ackdata))
newItem.setData(33, int(time.time()))
self.ui.tableWidgetSent.setItem(0, 3, newItem)
self.ui.textEditSentMessage.setPlainText(unicode(message, 'utf-8)'))
self.ui.tableWidgetSent.setSortingEnabled(True)
def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message):
subject = shared.fixPotentiallyInvalidUTF8Data(subject) if toAddress == str_broadcast_subscribers:
fromLabel = '' acct = accountClass(fromAddress)
queryreturn = sqlQuery(
'''select label from addressbook where address=?''', fromAddress)
if queryreturn != []:
for row in queryreturn:
fromLabel, = row
else: else:
# There might be a label in the subscriptions table acct = accountClass(toAddress)
queryreturn = sqlQuery( inbox = self.getAccountMessagelist(acct)
'''select label from subscriptions where address=?''', fromAddress) ret = None
if queryreturn != []: for treeWidget in [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans]:
for row in queryreturn: tableWidget = self.widgetConvert(treeWidget)
fromLabel, = row if tableWidget == inbox and self.getCurrentAccount(treeWidget) == acct.address and self.getCurrentFolder(treeWidget) in ["inbox", None]:
ret = self.addMessageListItemInbox(inbox, "inbox", inventoryHash, toAddress, fromAddress, subject, time.time(), 0)
try: elif treeWidget == self.ui.treeWidgetYourIdentities and self.getCurrentAccount(treeWidget) is None:
if toAddress == self.str_broadcast_subscribers: ret = self.addMessageListItemInbox(tableWidget, "inbox", inventoryHash, toAddress, fromAddress, subject, time.time(), 0)
toLabel = self.str_broadcast_subscribers if ret is None:
else: subject = shared.fixPotentiallyInvalidUTF8Data(subject)
toLabel = shared.config.get(toAddress, 'label') acct.parseMessage(toAddress, fromAddress, subject, "")
except:
toLabel = ''
if toLabel == '':
toLabel = toAddress
font = QFont()
font.setBold(True)
self.ui.tableWidgetInbox.setSortingEnabled(False)
newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8'))
newItem.setToolTip(unicode(toLabel, 'utf-8'))
newItem.setFont(font)
newItem.setData(Qt.UserRole, str(toAddress))
if shared.safeConfigGetBoolean(str(toAddress), 'mailinglist'):
newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta
if shared.safeConfigGetBoolean(str(toAddress), 'chan'):
newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange
self.ui.tableWidgetInbox.insertRow(0)
newItem.setIcon(avatarize(toAddress))
self.ui.tableWidgetInbox.setItem(0, 0, newItem)
if fromLabel == '':
newItem = QtGui.QTableWidgetItem(unicode(fromAddress, 'utf-8'))
newItem.setToolTip(unicode(fromAddress, 'utf-8'))
if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'):
self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(fromAddress, 'utf-8'), self.SOUND_UNKNOWN, None)
else: else:
newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) acct = ret
newItem.setToolTip(unicode(unicode(fromLabel, 'utf-8'))) self.propagateUnreadCount(acct.address)
if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'):
self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(fromLabel, 'utf-8'), self.SOUND_KNOWN, unicode(fromLabel, 'utf-8')) self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(acct.fromLabel, 'utf-8'), self.SOUND_UNKNOWN, None)
newItem.setData(Qt.UserRole, str(fromAddress)) if self.getCurrentAccount() is not None and ((self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address):
newItem.setFont(font) # Ubuntu should notify of new message irespective of whether it's in current message list or not
newItem.setIcon(avatarize(fromAddress)) self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel)
self.ui.tableWidgetInbox.setItem(0, 1, newItem) return
newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)'))
newItem.setToolTip(unicode(subject, 'utf-8)'))
#newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed.
newItem.setFont(font)
self.ui.tableWidgetInbox.setItem(0, 2, newItem)
newItem = myTableWidgetItem(l10n.formatTimestamp())
newItem.setToolTip(l10n.formatTimestamp())
newItem.setData(Qt.UserRole, QByteArray(inventoryHash))
newItem.setData(33, int(time.time()))
newItem.setFont(font)
self.ui.tableWidgetInbox.setItem(0, 3, newItem)
self.ui.tableWidgetInbox.setSortingEnabled(True)
self.ubuntuMessagingMenuUpdate(True, newItem, toLabel)
def click_pushButtonAddAddressBook(self): def click_pushButtonAddAddressBook(self):
self.AddAddressDialogInstance = AddAddressDialog(self) self.AddAddressDialogInstance = AddAddressDialog(self)
@ -2251,17 +2474,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f
def addEntryToAddressBook(self,address,label): def addEntryToAddressBook(self,address,label):
queryreturn = sqlQuery('''select * from addressbook where address=?''', address) queryreturn = sqlQuery('''select * from addressbook where address=?''', address)
if queryreturn == []: if queryreturn == []:
self.ui.tableWidgetAddressBook.setSortingEnabled(False)
self.ui.tableWidgetAddressBook.insertRow(0)
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
newItem.setIcon(avatarize(address))
self.ui.tableWidgetAddressBook.setItem(0, 0, newItem)
newItem = QtGui.QTableWidgetItem(address)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.ui.tableWidgetAddressBook.setItem(0, 1, newItem)
self.ui.tableWidgetAddressBook.setSortingEnabled(True)
sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', str(label), address) sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', str(label), address)
self.rerenderAddressBook()
self.rerenderInboxFromLabels() self.rerenderInboxFromLabels()
self.rerenderSentToLabels() self.rerenderSentToLabels()
else: else:
@ -2273,20 +2487,12 @@ more work your computer must do to send the message. A Time-To-Live of four or f
#This should be handled outside of this function, for error displaying and such, but it must also be checked here. #This should be handled outside of this function, for error displaying and such, but it must also be checked here.
if shared.isAddressInMySubscriptionsList(address): if shared.isAddressInMySubscriptionsList(address):
return return
#Add to UI list
self.ui.tableWidgetSubscriptions.setSortingEnabled(False)
self.ui.tableWidgetSubscriptions.insertRow(0)
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
newItem.setIcon(avatarize(address))
self.ui.tableWidgetSubscriptions.setItem(0,0,newItem)
newItem = QtGui.QTableWidgetItem(address)
newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled )
self.ui.tableWidgetSubscriptions.setItem(0,1,newItem)
self.ui.tableWidgetSubscriptions.setSortingEnabled(True)
#Add to database (perhaps this should be separated from the MyForm class) #Add to database (perhaps this should be separated from the MyForm class)
sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',str(label),address,True) sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',str(label),address,True)
self.rerenderInboxFromLabels() self.rerenderInboxFromLabels()
shared.reloadBroadcastSendersForWhichImWatching() shared.reloadBroadcastSendersForWhichImWatching()
self.rerenderAddressBook()
self.rerenderTabTreeSubscriptions()
def click_pushButtonAddSubscription(self): def click_pushButtonAddSubscription(self):
self.NewSubscriptionDialogInstance = NewSubscriptionDialog(self) self.NewSubscriptionDialogInstance = NewSubscriptionDialog(self)
@ -2297,7 +2503,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f
address = addBMIfNotPresent(str(self.NewSubscriptionDialogInstance.ui.lineEditSubscriptionAddress.text())) address = addBMIfNotPresent(str(self.NewSubscriptionDialogInstance.ui.lineEditSubscriptionAddress.text()))
# We must check to see if the address is already in the subscriptions list. The user cannot add it again or else it will cause problems when updating and deleting the entry. # We must check to see if the address is already in the subscriptions list. The user cannot add it again or else it will cause problems when updating and deleting the entry.
if shared.isAddressInMySubscriptionsList(address): if shared.isAddressInMySubscriptionsList(address):
self.statusBar().showMessage(_translate("MainWindow", "Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want.")) self.statusBar().showMessage(_translate("MainWindow", "Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want."))
return return
label = self.NewSubscriptionDialogInstance.ui.newsubscriptionlabel.text().toUtf8() label = self.NewSubscriptionDialogInstance.ui.newsubscriptionlabel.text().toUtf8()
self.addSubscription(address, label) self.addSubscription(address, label)
@ -2319,7 +2525,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f
shared.objectProcessorQueue.put((objectType,payload)) shared.objectProcessorQueue.put((objectType,payload))
def click_pushButtonStatusIcon(self): def click_pushButtonStatusIcon(self):
print 'click_pushButtonStatusIcon' logger.debug('click_pushButtonStatusIcon')
self.iconGlossaryInstance = iconGlossaryDialog(self) self.iconGlossaryInstance = iconGlossaryDialog(self)
if self.iconGlossaryInstance.exec_(): if self.iconGlossaryInstance.exec_():
pass pass
@ -2361,6 +2567,12 @@ more work your computer must do to send the message. A Time-To-Live of four or f
"MainWindow", "You must restart Bitmessage for the port number change to take effect.")) "MainWindow", "You must restart Bitmessage for the port number change to take effect."))
shared.config.set('bitmessagesettings', 'port', str( shared.config.set('bitmessagesettings', 'port', str(
self.settingsDialogInstance.ui.lineEditTCPPort.text())) self.settingsDialogInstance.ui.lineEditTCPPort.text()))
if self.settingsDialogInstance.ui.checkBoxUPnP.isChecked() != shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'):
shared.config.set('bitmessagesettings', 'upnp', str(self.settingsDialogInstance.ui.checkBoxUPnP.isChecked()))
if self.settingsDialogInstance.ui.checkBoxUPnP.isChecked():
import upnp
upnpThread = upnp.uPnPThread()
upnpThread.start()
#print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText()', self.settingsDialogInstance.ui.comboBoxProxyType.currentText() #print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText()', self.settingsDialogInstance.ui.comboBoxProxyType.currentText()
#print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText())[0:5]', self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] #print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText())[0:5]', self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5]
if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS': if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS':
@ -2415,6 +2627,9 @@ more work your computer must do to send the message. A Time-To-Live of four or f
shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(int(float( shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(int(float(
self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) * shared.networkDefaultPayloadLengthExtraBytes))) self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) * shared.networkDefaultPayloadLengthExtraBytes)))
if openclpow.has_opencl() and self.settingsDialogInstance.ui.checkBoxOpenCL.isChecked() != shared.safeConfigGetBoolean("bitmessagesettings", "opencl"):
shared.config.set('bitmessagesettings', 'opencl', str(self.settingsDialogInstance.ui.checkBoxOpenCL.isChecked()))
acceptableDifficultyChanged = False acceptableDifficultyChanged = False
if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) == 0: if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) == 0:
@ -2597,9 +2812,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f
self.dialog = SpecialAddressBehaviorDialog(self) self.dialog = SpecialAddressBehaviorDialog(self)
# For Modal dialogs # For Modal dialogs
if self.dialog.exec_(): if self.dialog.exec_():
currentRow = self.ui.tableWidgetYourIdentities.currentRow() addressAtCurrentRow = self.getCurrentAccount()
addressAtCurrentRow = str(
self.ui.tableWidgetYourIdentities.item(currentRow, 1).text())
if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'):
return return
if self.dialog.ui.radioButtonBehaveNormalAddress.isChecked(): if self.dialog.ui.radioButtonBehaveNormalAddress.isChecked():
@ -2607,22 +2820,57 @@ more work your computer must do to send the message. A Time-To-Live of four or f
addressAtCurrentRow), 'mailinglist', 'false') addressAtCurrentRow), 'mailinglist', 'false')
# Set the color to either black or grey # Set the color to either black or grey
if shared.config.getboolean(addressAtCurrentRow, 'enabled'): if shared.config.getboolean(addressAtCurrentRow, 'enabled'):
self.ui.tableWidgetYourIdentities.item( self.setCurrentItemColor(QApplication.palette()
currentRow, 1).setTextColor(QApplication.palette()
.text().color()) .text().color())
else: else:
self.ui.tableWidgetYourIdentities.item( self.setCurrentItemColor(QtGui.QColor(128, 128, 128))
currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128))
else: else:
shared.config.set(str( shared.config.set(str(
addressAtCurrentRow), 'mailinglist', 'true') addressAtCurrentRow), 'mailinglist', 'true')
shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str( shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str(
self.dialog.ui.lineEditMailingListName.text().toUtf8())) self.dialog.ui.lineEditMailingListName.text().toUtf8()))
self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta self.setCurrentItemColor(QtGui.QColor(137, 04, 177)) #magenta
self.rerenderComboBoxSendFrom()
self.rerenderComboBoxSendFromBroadcast()
shared.writeKeysFile() shared.writeKeysFile()
self.rerenderInboxToLabels() self.rerenderInboxToLabels()
def on_action_EmailGatewayDialog(self):
self.dialog = EmailGatewayDialog(self)
# For Modal dialogs
if self.dialog.exec_():
addressAtCurrentRow = self.getCurrentAccount()
acct = accountClass(addressAtCurrentRow)
# no chans / mailinglists
if acct.type != AccountMixin.NORMAL:
return
if self.dialog.ui.radioButtonUnregister.isChecked() and isinstance(acct, GatewayAccount):
acct.unregister()
shared.config.remove_option(addressAtCurrentRow, 'gateway')
shared.writeKeysFile()
elif self.dialog.ui.radioButtonRegister.isChecked():
email = str(self.dialog.ui.lineEditEmail.text().toUtf8())
acct = MailchuckAccount(addressAtCurrentRow)
acct.register(email)
shared.config.set(addressAtCurrentRow, 'label', email)
shared.config.set(addressAtCurrentRow, 'gateway', 'mailchuck')
self.getCurrentTreeWidget().currentItem().updateText()
shared.writeKeysFile()
else:
pass
#print "well nothing"
# shared.writeKeysFile()
# self.rerenderInboxToLabels()
def click_NewAddressDialog(self): def click_NewAddressDialog(self):
addresses = []
for addressInKeysFile in getSortedAccounts():
addresses.append(addressInKeysFile)
# self.dialog = Ui_NewAddressWizard(addresses)
# self.dialog.exec_()
# print "Name: " + self.dialog.field("name").toString()
# print "Email: " + self.dialog.field("email").toString()
# return
self.dialog = NewAddressDialog(self) self.dialog = NewAddressDialog(self)
# For Modal dialogs # For Modal dialogs
if self.dialog.exec_(): if self.dialog.exec_():
@ -2649,7 +2897,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f
shared.addressGeneratorQueue.put(('createDeterministicAddresses', 4, streamNumberForAddress, "unused deterministic address", self.dialog.ui.spinBoxNumberOfAddressesToMake.value( shared.addressGeneratorQueue.put(('createDeterministicAddresses', 4, streamNumberForAddress, "unused deterministic address", self.dialog.ui.spinBoxNumberOfAddressesToMake.value(
), self.dialog.ui.lineEditPassphrase.text().toUtf8(), self.dialog.ui.checkBoxEighteenByteRipe.isChecked())) ), self.dialog.ui.lineEditPassphrase.text().toUtf8(), self.dialog.ui.checkBoxEighteenByteRipe.isChecked()))
else: else:
print 'new address dialog box rejected' logger.debug('new address dialog box rejected')
# Quit selected from menu or application indicator # Quit selected from menu or application indicator
def quit(self): def quit(self):
@ -2660,11 +2908,24 @@ more work your computer must do to send the message. A Time-To-Live of four or f
if reply is QtGui.QMessageBox.No: if reply is QtGui.QMessageBox.No:
return return
''' '''
# save state and geometry self and all widgets
self.saveSettings()
for attr, obj in self.ui.__dict__.iteritems():
if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin):
saveMethod = getattr(obj, "saveSettings", None)
if callable (saveMethod):
obj.saveSettings()
shared.doCleanShutdown() shared.doCleanShutdown()
self.tray.hide() self.tray.hide()
# unregister the messaging system # unregister the messaging system
if self.mmapp is not None: if self.mmapp is not None:
self.mmapp.unregister() self.mmapp.unregister()
# settings = QSettings("Bitmessage", "PyBitmessage")
# settings.setValue("geometry", self.saveGeometry())
# settings.setValue("state", self.saveState())
self.statusBar().showMessage(_translate( self.statusBar().showMessage(_translate(
"MainWindow", "All done. Closing user interface...")) "MainWindow", "All done. Closing user interface..."))
os._exit(0) os._exit(0)
@ -2689,10 +2950,10 @@ more work your computer must do to send the message. A Time-To-Live of four or f
self.quit() self.quit()
def on_action_InboxMessageForceHtml(self): def on_action_InboxMessageForceHtml(self):
currentInboxRow = self.ui.tableWidgetInbox.currentRow() msgid = self.getCurrentMessageId()
textEdit = self.getCurrentMessageTextedit()
msgid = str(self.ui.tableWidgetInbox.item( if not msgid:
currentInboxRow, 3).data(Qt.UserRole).toPyObject()) return
queryreturn = sqlQuery( queryreturn = sqlQuery(
'''select message from inbox where msgid=?''', msgid) '''select message from inbox where msgid=?''', msgid)
if queryreturn != []: if queryreturn != []:
@ -2713,29 +2974,42 @@ more work your computer must do to send the message. A Time-To-Live of four or f
content = ' '.join(lines) # To keep the whitespace between lines content = ' '.join(lines) # To keep the whitespace between lines
content = shared.fixPotentiallyInvalidUTF8Data(content) content = shared.fixPotentiallyInvalidUTF8Data(content)
content = unicode(content, 'utf-8)') content = unicode(content, 'utf-8)')
self.ui.textEditInboxMessage.setHtml(QtCore.QString(content)) textEdit.setHtml(QtCore.QString(content))
def on_action_InboxMarkUnread(self): def on_action_InboxMarkUnread(self):
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
font = QFont() font = QFont()
font.setBold(True) font.setBold(True)
inventoryHashesToMarkUnread = [] inventoryHashesToMarkUnread = []
for row in self.ui.tableWidgetInbox.selectedIndexes(): modified = 0
for row in tableWidget.selectedIndexes():
currentRow = row.row() currentRow = row.row()
inventoryHashToMarkUnread = str(self.ui.tableWidgetInbox.item( inventoryHashToMarkUnread = str(tableWidget.item(
currentRow, 3).data(Qt.UserRole).toPyObject()) currentRow, 3).data(Qt.UserRole).toPyObject())
if inventoryHashToMarkUnread in inventoryHashesToMarkUnread:
# it returns columns as separate items, so we skip dupes
continue
if not tableWidget.item(currentRow, 0).font().bold():
modified += 1
inventoryHashesToMarkUnread.append(inventoryHashToMarkUnread) inventoryHashesToMarkUnread.append(inventoryHashToMarkUnread)
self.ui.tableWidgetInbox.item(currentRow, 0).setFont(font) tableWidget.item(currentRow, 0).setFont(font)
self.ui.tableWidgetInbox.item(currentRow, 1).setFont(font) tableWidget.item(currentRow, 1).setFont(font)
self.ui.tableWidgetInbox.item(currentRow, 2).setFont(font) tableWidget.item(currentRow, 2).setFont(font)
self.ui.tableWidgetInbox.item(currentRow, 3).setFont(font) tableWidget.item(currentRow, 3).setFont(font)
#sqlite requires the exact number of ?s to prevent injection #sqlite requires the exact number of ?s to prevent injection
sqlExecute('''UPDATE inbox SET read=0 WHERE msgid IN (%s)''' % ( sqlExecute('''UPDATE inbox SET read=0 WHERE msgid IN (%s)''' % (
"?," * len(inventoryHashesToMarkUnread))[:-1], *inventoryHashesToMarkUnread) "?," * len(inventoryHashesToMarkUnread))[:-1], *inventoryHashesToMarkUnread)
self.changedInboxUnread() if modified == 1:
# self.ui.tableWidgetInbox.selectRow(currentRow + 1) # performance optimisation
self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), self.getCurrentFolder())
else:
self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), self.getCurrentFolder(), self.getCurrentTreeWidget(), 0)
# tableWidget.selectRow(currentRow + 1)
# This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary. # This doesn't de-select the last message if you try to mark it unread, but that doesn't interfere. Might not be necessary.
# We could also select upwards, but then our problem would be with the topmost message. # We could also select upwards, but then our problem would be with the topmost message.
# self.ui.tableWidgetInbox.clearSelection() manages to mark the message as read again. # tableWidget.clearSelection() manages to mark the message as read again.
# Format predefined text on message reply. # Format predefined text on message reply.
def quoted_text(self, message): def quoted_text(self, message):
@ -2760,112 +3034,186 @@ more work your computer must do to send the message. A Time-To-Live of four or f
return '\n'.join([quote_line(l) for l in message.splitlines()]) + '\n\n' return '\n'.join([quote_line(l) for l in message.splitlines()]) + '\n\n'
def on_action_InboxReply(self): def on_action_InboxReply(self):
currentInboxRow = self.ui.tableWidgetInbox.currentRow() tableWidget = self.getCurrentMessagelist()
toAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( if not tableWidget:
return
# save this to return back after reply is done
self.replyFromTab = self.ui.tabWidget.currentIndex()
currentInboxRow = tableWidget.currentRow()
toAddressAtCurrentInboxRow = str(tableWidget.item(
currentInboxRow, 0).data(Qt.UserRole).toPyObject()) currentInboxRow, 0).data(Qt.UserRole).toPyObject())
fromAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( acct = accountClass(toAddressAtCurrentInboxRow)
fromAddressAtCurrentInboxRow = str(tableWidget.item(
currentInboxRow, 1).data(Qt.UserRole).toPyObject()) currentInboxRow, 1).data(Qt.UserRole).toPyObject())
msgid = str(self.ui.tableWidgetInbox.item( msgid = str(tableWidget.item(
currentInboxRow, 3).data(Qt.UserRole).toPyObject()) currentInboxRow, 3).data(Qt.UserRole).toPyObject())
queryreturn = sqlQuery( queryreturn = sqlQuery(
'''select message from inbox where msgid=?''', msgid) '''select message from inbox where msgid=?''', msgid)
if queryreturn != []: if queryreturn != []:
for row in queryreturn: for row in queryreturn:
messageAtCurrentInboxRow, = row messageAtCurrentInboxRow, = row
if toAddressAtCurrentInboxRow == self.str_broadcast_subscribers: acct.parseMessage(toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow, unicode(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toPyObject(), 'utf-8'), messageAtCurrentInboxRow)
self.ui.labelFrom.setText('') widget = {
'subject': self.ui.lineEditSubject,
'from': self.ui.comboBoxSendFrom,
'message': self.ui.textEditMessage
}
if toAddressAtCurrentInboxRow == str_broadcast_subscribers:
self.ui.tabWidgetSend.setCurrentIndex(0)
# toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
elif not shared.config.has_section(toAddressAtCurrentInboxRow): elif not shared.config.has_section(toAddressAtCurrentInboxRow):
QtGui.QMessageBox.information(self, _translate("MainWindow", "Address is gone"), _translate( QtGui.QMessageBox.information(self, _translate("MainWindow", "Address is gone"), _translate(
"MainWindow", "Bitmessage cannot find your address %1. Perhaps you removed it?").arg(toAddressAtCurrentInboxRow), QMessageBox.Ok) "MainWindow", "Bitmessage cannot find your address %1. Perhaps you removed it?").arg(toAddressAtCurrentInboxRow), QMessageBox.Ok)
self.ui.labelFrom.setText('')
elif not shared.config.getboolean(toAddressAtCurrentInboxRow, 'enabled'): elif not shared.config.getboolean(toAddressAtCurrentInboxRow, 'enabled'):
QtGui.QMessageBox.information(self, _translate("MainWindow", "Address disabled"), _translate( QtGui.QMessageBox.information(self, _translate("MainWindow", "Address disabled"), _translate(
"MainWindow", "Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it."), QMessageBox.Ok) "MainWindow", "Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it."), QMessageBox.Ok)
self.ui.labelFrom.setText('')
else: else:
self.ui.labelFrom.setText(toAddressAtCurrentInboxRow) self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(toAddressAtCurrentInboxRow)
self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(toAddressAtCurrentInboxRow) if self.ui.tabWidgetSend.currentIndex() == 1:
widget = {
'subject': self.ui.lineEditSubjectBroadcast,
'from': self.ui.comboBoxSendFromBroadcast,
'message': self.ui.textEditMessageBroadcast
}
self.ui.tabWidgetSend.setCurrentIndex(1)
toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
self.ui.lineEditTo.setText(str(fromAddressAtCurrentInboxRow)) self.ui.lineEditTo.setText(str(acct.fromAddress))
# If the previous message was to a chan then we should send our reply to the chan rather than to the particular person who sent the message. # If the previous message was to a chan then we should send our reply to the chan rather than to the particular person who sent the message.
if shared.config.has_section(toAddressAtCurrentInboxRow): if shared.config.has_section(toAddressAtCurrentInboxRow):
if shared.safeConfigGetBoolean(toAddressAtCurrentInboxRow, 'chan'): if shared.safeConfigGetBoolean(toAddressAtCurrentInboxRow, 'chan'):
print 'original sent to a chan. Setting the to address in the reply to the chan address.' logger.debug('original sent to a chan. Setting the to address in the reply to the chan address.')
self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow)) self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow))
listOfAddressesInComboBoxSendFrom = [str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) for i in range(self.ui.comboBoxSendFrom.count())] listOfAddressesInComboBoxSendFrom = [str(widget['from'].itemData(i).toPyObject()) for i in range(widget['from'].count())]
if toAddressAtCurrentInboxRow in listOfAddressesInComboBoxSendFrom: if toAddressAtCurrentInboxRow in listOfAddressesInComboBoxSendFrom:
currentIndex = listOfAddressesInComboBoxSendFrom.index(toAddressAtCurrentInboxRow) currentIndex = listOfAddressesInComboBoxSendFrom.index(toAddressAtCurrentInboxRow)
self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) widget['from'].setCurrentIndex(currentIndex)
else: else:
self.ui.comboBoxSendFrom.setCurrentIndex(0) widget['from'].setCurrentIndex(0)
quotedText = self.quoted_text(unicode(messageAtCurrentInboxRow, 'utf-8')) quotedText = self.quoted_text(unicode(messageAtCurrentInboxRow, 'utf-8'))
self.ui.textEditMessage.setText(quotedText) widget['message'].setText(quotedText)
if self.ui.tableWidgetInbox.item(currentInboxRow, 2).text()[0:3] in ['Re:', 'RE:']: if acct.subject[0:3] in ['Re:', 'RE:']:
self.ui.lineEditSubject.setText( widget['subject'].setText(acct.subject)
self.ui.tableWidgetInbox.item(currentInboxRow, 2).text())
else: else:
self.ui.lineEditSubject.setText( widget['subject'].setText('Re: ' + acct.subject)
'Re: ' + self.ui.tableWidgetInbox.item(currentInboxRow, 2).text())
self.ui.radioButtonSpecific.setChecked(True)
self.ui.tabWidget.setCurrentIndex(1) self.ui.tabWidget.setCurrentIndex(1)
def on_action_InboxAddSenderToAddressBook(self): def on_action_InboxAddSenderToAddressBook(self):
currentInboxRow = self.ui.tableWidgetInbox.currentRow() tableWidget = self.getCurrentMessagelist()
# self.ui.tableWidgetInbox.item(currentRow,1).data(Qt.UserRole).toPyObject() if not tableWidget:
addressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( return
currentInboxRow = tableWidget.currentRow()
# tableWidget.item(currentRow,1).data(Qt.UserRole).toPyObject()
addressAtCurrentInboxRow = str(tableWidget.item(
currentInboxRow, 1).data(Qt.UserRole).toPyObject()) currentInboxRow, 1).data(Qt.UserRole).toPyObject())
# Let's make sure that it isn't already in the address book # Let's make sure that it isn't already in the address book
queryreturn = sqlQuery('''select * from addressbook where address=?''', queryreturn = sqlQuery('''select * from addressbook where address=?''',
addressAtCurrentInboxRow) addressAtCurrentInboxRow)
if queryreturn == []: if queryreturn == []:
self.ui.tableWidgetAddressBook.insertRow(0)
newItem = QtGui.QTableWidgetItem(
'--New entry. Change label in Address Book.--')
self.ui.tableWidgetAddressBook.setItem(0, 0, newItem)
newItem.setIcon(avatarize(addressAtCurrentInboxRow))
newItem = QtGui.QTableWidgetItem(addressAtCurrentInboxRow)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.ui.tableWidgetAddressBook.setItem(0, 1, newItem)
sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', sqlExecute('''INSERT INTO addressbook VALUES (?,?)''',
'--New entry. Change label in Address Book.--', '--New entry. Change label in Address Book.--',
addressAtCurrentInboxRow) addressAtCurrentInboxRow)
self.ui.tabWidget.setCurrentIndex(5) self.rerenderAddressBook()
self.ui.tableWidgetAddressBook.setCurrentCell(0, 0)
self.statusBar().showMessage(_translate( self.statusBar().showMessage(_translate(
"MainWindow", "Entry added to the Address Book. Edit the label to your liking.")) "MainWindow", "Entry added to the Address Book. Edit the label to your liking."))
else: else:
self.statusBar().showMessage(_translate( self.statusBar().showMessage(_translate(
"MainWindow", "Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want.")) "MainWindow", "Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want."))
def on_action_InboxAddSenderToBlackList(self):
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
currentInboxRow = tableWidget.currentRow()
# tableWidget.item(currentRow,1).data(Qt.UserRole).toPyObject()
addressAtCurrentInboxRow = str(tableWidget.item(
currentInboxRow, 1).data(Qt.UserRole).toPyObject())
# Let's make sure that it isn't already in the address book
queryreturn = sqlQuery('''select * from blacklist where address=?''',
addressAtCurrentInboxRow)
if queryreturn == []:
label = "\"" + unicode(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toString(), 'utf-8') + "\" in " + shared.config.get(self.getCurrentAccount(), "label")
sqlExecute('''INSERT INTO blacklist VALUES (?,?, ?)''',
label,
addressAtCurrentInboxRow, True)
self.rerenderBlackWhiteList()
self.statusBar().showMessage(_translate(
"MainWindow", "Entry added to the blacklist. Edit the label to your liking."))
else:
self.statusBar().showMessage(_translate(
"MainWindow", "Error: You cannot add the same address to your blacklist twice. Try renaming the existing one if you want."))
# Send item on the Inbox tab to trash # Send item on the Inbox tab to trash
def on_action_InboxTrash(self): def on_action_InboxTrash(self):
while self.ui.tableWidgetInbox.selectedIndexes() != []: tableWidget = self.getCurrentMessagelist()
currentRow = self.ui.tableWidgetInbox.selectedIndexes()[0].row() if not tableWidget:
inventoryHashToTrash = str(self.ui.tableWidgetInbox.item( return
unread = False
currentRow = 0
folder = self.getCurrentFolder()
shifted = QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier
while tableWidget.selectedIndexes():
currentRow = tableWidget.selectedIndexes()[0].row()
inventoryHashToTrash = str(tableWidget.item(
currentRow, 3).data(Qt.UserRole).toPyObject()) currentRow, 3).data(Qt.UserRole).toPyObject())
sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash) if folder == "trash" or shifted:
self.ui.textEditInboxMessage.setText("") sqlExecute('''DELETE FROM inbox WHERE msgid=?''', inventoryHashToTrash)
self.ui.tableWidgetInbox.removeRow(currentRow) else:
sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash)
if tableWidget.item(currentRow, 0).font().bold():
self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), folder, self.getCurrentTreeWidget(), -1)
if folder != "trash" and not shifted:
self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), "trash", self.getCurrentTreeWidget(), 1)
self.getCurrentMessageTextedit().setText("")
tableWidget.removeRow(currentRow)
self.statusBar().showMessage(_translate( self.statusBar().showMessage(_translate(
"MainWindow", "Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.")) "MainWindow", "Moved items to trash."))
if currentRow == 0: if currentRow == 0:
self.ui.tableWidgetInbox.selectRow(currentRow) tableWidget.selectRow(currentRow)
else: else:
self.ui.tableWidgetInbox.selectRow(currentRow - 1) tableWidget.selectRow(currentRow - 1)
def on_action_TrashUndelete(self):
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
unread = False
currentRow = 0
while tableWidget.selectedIndexes():
currentRow = tableWidget.selectedIndexes()[0].row()
inventoryHashToTrash = str(tableWidget.item(
currentRow, 3).data(Qt.UserRole).toPyObject())
sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=?''', inventoryHashToTrash)
if tableWidget.item(currentRow, 0).font().bold():
self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), "inbox", self.getCurrentTreeWidget(), 1)
self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), "trash", self.getCurrentTreeWidget(), -1)
self.getCurrentMessageTextedit().setText("")
tableWidget.removeRow(currentRow)
self.statusBar().showMessage(_translate(
"MainWindow", "Undeleted item."))
if currentRow == 0:
tableWidget.selectRow(currentRow)
else:
tableWidget.selectRow(currentRow - 1)
def on_action_InboxSaveMessageAs(self): def on_action_InboxSaveMessageAs(self):
currentInboxRow = self.ui.tableWidgetInbox.currentRow() tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
currentInboxRow = tableWidget.currentRow()
try: try:
subjectAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item(currentInboxRow,2).text()) subjectAtCurrentInboxRow = str(tableWidget.item(currentInboxRow,2).text())
except: except:
subjectAtCurrentInboxRow = '' subjectAtCurrentInboxRow = ''
# Retrieve the message data out of the SQL database # Retrieve the message data out of the SQL database
msgid = str(self.ui.tableWidgetInbox.item( msgid = str(tableWidget.item(
currentInboxRow, 3).data(Qt.UserRole).toPyObject()) currentInboxRow, 3).data(Qt.UserRole).toPyObject())
queryreturn = sqlQuery( queryreturn = sqlQuery(
'''select message from inbox where msgid=?''', msgid) '''select message from inbox where msgid=?''', msgid)
@ -2882,28 +3230,40 @@ more work your computer must do to send the message. A Time-To-Live of four or f
f.write(message) f.write(message)
f.close() f.close()
except Exception, e: except Exception, e:
sys.stderr.write('Write error: '+ e) logger.exception('Message not saved', exc_info=True)
self.statusBar().showMessage(_translate("MainWindow", "Write error.")) self.statusBar().showMessage(_translate("MainWindow", "Write error."))
# Send item on the Sent tab to trash # Send item on the Sent tab to trash
def on_action_SentTrash(self): def on_action_SentTrash(self):
while self.ui.tableWidgetSent.selectedIndexes() != []: currentRow = 0
currentRow = self.ui.tableWidgetSent.selectedIndexes()[0].row() unread = False
ackdataToTrash = str(self.ui.tableWidgetSent.item( tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
folder = self.getCurrentFolder()
shifted = (QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier) > 0
while tableWidget.selectedIndexes() != []:
currentRow = tableWidget.selectedIndexes()[0].row()
ackdataToTrash = str(tableWidget.item(
currentRow, 3).data(Qt.UserRole).toPyObject()) currentRow, 3).data(Qt.UserRole).toPyObject())
sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash) if folder == "trash" or shifted:
self.ui.textEditSentMessage.setPlainText("") sqlExecute('''DELETE FROM sent WHERE ackdata=?''', ackdataToTrash)
self.ui.tableWidgetSent.removeRow(currentRow) else:
sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash)
if tableWidget.item(currentRow, 0).font().bold():
self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), folder, self.getCurrentTreeWidget(), -1)
self.getCurrentMessageTextedit().setPlainText("")
tableWidget.removeRow(currentRow)
self.statusBar().showMessage(_translate( self.statusBar().showMessage(_translate(
"MainWindow", "Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.")) "MainWindow", "Moved items to trash."))
if currentRow == 0: if currentRow == 0:
self.ui.tableWidgetSent.selectRow(currentRow) self.ui.tableWidgetInbox.selectRow(currentRow)
else: else:
self.ui.tableWidgetSent.selectRow(currentRow - 1) self.ui.tableWidgetInbox.selectRow(currentRow - 1)
def on_action_ForceSend(self): def on_action_ForceSend(self):
currentRow = self.ui.tableWidgetSent.currentRow() currentRow = self.ui.tableWidgetInbox.currentRow()
addressAtCurrentRow = str(self.ui.tableWidgetSent.item( addressAtCurrentRow = str(self.ui.tableWidgetInbox.item(
currentRow, 0).data(Qt.UserRole).toPyObject()) currentRow, 0).data(Qt.UserRole).toPyObject())
toRipe = decodeAddress(addressAtCurrentRow)[3] toRipe = decodeAddress(addressAtCurrentRow)[3]
sqlExecute( sqlExecute(
@ -2917,8 +3277,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f
shared.workerQueue.put(('sendmessage', '')) shared.workerQueue.put(('sendmessage', ''))
def on_action_SentClipboard(self): def on_action_SentClipboard(self):
currentRow = self.ui.tableWidgetSent.currentRow() currentRow = self.ui.tableWidgetInbox.currentRow()
addressAtCurrentRow = str(self.ui.tableWidgetSent.item( addressAtCurrentRow = str(self.ui.tableWidgetInbox.item(
currentRow, 0).data(Qt.UserRole).toPyObject()) currentRow, 0).data(Qt.UserRole).toPyObject())
clipboard = QtGui.QApplication.clipboard() clipboard = QtGui.QApplication.clipboard()
clipboard.setText(str(addressAtCurrentRow)) clipboard.setText(str(addressAtCurrentRow))
@ -2985,13 +3345,30 @@ more work your computer must do to send the message. A Time-To-Live of four or f
addressAtCurrentRow = str(self.ui.tableWidgetAddressBook.item(currentRow,1).text()) addressAtCurrentRow = str(self.ui.tableWidgetAddressBook.item(currentRow,1).text())
# Then subscribe to it... provided it's not already in the address book # Then subscribe to it... provided it's not already in the address book
if shared.isAddressInMySubscriptionsList(addressAtCurrentRow): if shared.isAddressInMySubscriptionsList(addressAtCurrentRow):
self.statusBar().showMessage(QtGui.QApplication.translate("MainWindow", "Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want.")) self.statusBar().showMessage(QtGui.QApplication.translate("MainWindow", "Error: You cannot add the same address to your subscriptions twice. Perhaps rename the existing one if you want."))
continue continue
labelAtCurrentRow = self.ui.tableWidgetAddressBook.item(currentRow,0).text().toUtf8() labelAtCurrentRow = self.ui.tableWidgetAddressBook.item(currentRow,0).text().toUtf8()
self.addSubscription(addressAtCurrentRow, labelAtCurrentRow) self.addSubscription(addressAtCurrentRow, labelAtCurrentRow)
self.ui.tabWidget.setCurrentIndex(4) self.ui.tabWidget.setCurrentIndex(4)
def on_context_menuAddressBook(self, point): def on_context_menuAddressBook(self, point):
self.popMenuAddressBook = QtGui.QMenu(self)
self.popMenuAddressBook.addAction(self.actionAddressBookSend)
self.popMenuAddressBook.addAction(self.actionAddressBookClipboard)
self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe)
self.popMenuAddressBook.addAction(self.actionAddressBookSetAvatar)
self.popMenuAddressBook.addSeparator()
self.popMenuAddressBook.addAction(self.actionAddressBookNew)
normal = True
for row in self.ui.tableWidgetAddressBook.selectedIndexes():
currentRow = row.row()
type = self.ui.tableWidgetAddressBook.item(
currentRow, 0).type
if type != AccountMixin.NORMAL:
normal = False
if normal:
# only if all selected addressbook items are normal, allow delete
self.popMenuAddressBook.addAction(self.actionAddressBookDelete)
self.popMenuAddressBook.exec_( self.popMenuAddressBook.exec_(
self.ui.tableWidgetAddressBook.mapToGlobal(point)) self.ui.tableWidgetAddressBook.mapToGlobal(point))
@ -3000,58 +3377,55 @@ more work your computer must do to send the message. A Time-To-Live of four or f
self.click_pushButtonAddSubscription() self.click_pushButtonAddSubscription()
def on_action_SubscriptionsDelete(self): def on_action_SubscriptionsDelete(self):
print 'clicked Delete' if QtGui.QMessageBox.question(self, "Delete subscription?", _translate("MainWindow", "If you delete the subscription, messages that you already received will become inaccessible. Maybe you can consider disabling the subscription instead. Disabled subscriptions will not receive new messages, but you can still view messages you already received.\n\nAre you sure you want to delete the subscription?"), QMessageBox.Yes|QMessageBox.No) != QMessageBox.Yes:
currentRow = self.ui.tableWidgetSubscriptions.currentRow() return
labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item( address = self.getCurrentAccount()
currentRow, 0).text().toUtf8() sqlExecute('''DELETE FROM subscriptions WHERE address=?''',
addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item( address)
currentRow, 1).text() self.rerenderTabTreeSubscriptions()
sqlExecute('''DELETE FROM subscriptions WHERE label=? AND address=?''',
str(labelAtCurrentRow), str(addressAtCurrentRow))
self.ui.tableWidgetSubscriptions.removeRow(currentRow)
self.rerenderInboxFromLabels() self.rerenderInboxFromLabels()
self.rerenderAddressBook()
shared.reloadBroadcastSendersForWhichImWatching() shared.reloadBroadcastSendersForWhichImWatching()
def on_action_SubscriptionsClipboard(self): def on_action_SubscriptionsClipboard(self):
currentRow = self.ui.tableWidgetSubscriptions.currentRow() address = self.getCurrentAccount()
addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
currentRow, 1).text()
clipboard = QtGui.QApplication.clipboard() clipboard = QtGui.QApplication.clipboard()
clipboard.setText(str(addressAtCurrentRow)) clipboard.setText(str(address))
def on_action_SubscriptionsEnable(self): def on_action_SubscriptionsEnable(self):
currentRow = self.ui.tableWidgetSubscriptions.currentRow() address = self.getCurrentAccount()
labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
currentRow, 0).text().toUtf8()
addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
currentRow, 1).text()
sqlExecute( sqlExecute(
'''update subscriptions set enabled=1 WHERE label=? AND address=?''', '''update subscriptions set enabled=1 WHERE address=?''',
str(labelAtCurrentRow), str(addressAtCurrentRow)) address)
self.ui.tableWidgetSubscriptions.item( account = self.getCurrentItem()
currentRow, 0).setTextColor(QApplication.palette().text().color()) account.setEnabled(True)
self.ui.tableWidgetSubscriptions.item( self.rerenderAddressBook()
currentRow, 1).setTextColor(QApplication.palette().text().color())
shared.reloadBroadcastSendersForWhichImWatching() shared.reloadBroadcastSendersForWhichImWatching()
def on_action_SubscriptionsDisable(self): def on_action_SubscriptionsDisable(self):
currentRow = self.ui.tableWidgetSubscriptions.currentRow() address = self.getCurrentAccount()
labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
currentRow, 0).text().toUtf8()
addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
currentRow, 1).text()
sqlExecute( sqlExecute(
'''update subscriptions set enabled=0 WHERE label=? AND address=?''', '''update subscriptions set enabled=0 WHERE address=?''',
str(labelAtCurrentRow), str(addressAtCurrentRow)) address)
self.ui.tableWidgetSubscriptions.item( account = self.getCurrentItem()
currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128)) account.setEnabled(False)
self.ui.tableWidgetSubscriptions.item( self.rerenderAddressBook()
currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128))
shared.reloadBroadcastSendersForWhichImWatching() shared.reloadBroadcastSendersForWhichImWatching()
def on_context_menuSubscriptions(self, point): def on_context_menuSubscriptions(self, point):
self.popMenuSubscriptions = QtGui.QMenu(self)
self.popMenuSubscriptions.addAction(self.actionsubscriptionsNew)
self.popMenuSubscriptions.addAction(self.actionsubscriptionsDelete)
self.popMenuSubscriptions.addSeparator()
if self.getCurrentItem().isEnabled:
self.popMenuSubscriptions.addAction(self.actionsubscriptionsDisable)
else:
self.popMenuSubscriptions.addAction(self.actionsubscriptionsEnable)
self.popMenuSubscriptions.addAction(self.actionsubscriptionsSetAvatar)
self.popMenuSubscriptions.addSeparator()
self.popMenuSubscriptions.addAction(self.actionsubscriptionsClipboard)
self.popMenuSubscriptions.exec_( self.popMenuSubscriptions.exec_(
self.ui.tableWidgetSubscriptions.mapToGlobal(point)) self.ui.treeWidgetSubscriptions.mapToGlobal(point))
# Group of functions for the Blacklist dialog box # Group of functions for the Blacklist dialog box
def on_action_BlacklistNew(self): def on_action_BlacklistNew(self):
@ -3116,70 +3490,243 @@ more work your computer must do to send the message. A Time-To-Live of four or f
sqlExecute( sqlExecute(
'''UPDATE whitelist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow)) '''UPDATE whitelist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow))
def widgetConvert (self, widget):
if widget == self.ui.tableWidgetInbox:
return self.ui.treeWidgetYourIdentities
elif widget == self.ui.tableWidgetInboxSubscriptions:
return self.ui.treeWidgetSubscriptions
elif widget == self.ui.tableWidgetInboxChans:
return self.ui.treeWidgetChans
elif widget == self.ui.treeWidgetYourIdentities:
return self.ui.tableWidgetInbox
elif widget == self.ui.treeWidgetSubscriptions:
return self.ui.tableWidgetInboxSubscriptions
elif widget == self.ui.treeWidgetChans:
return self.ui.tableWidgetInboxChans
else:
return None
def getCurrentTreeWidget(self):
currentIndex = self.ui.tabWidget.currentIndex();
treeWidgetList = [
self.ui.treeWidgetYourIdentities,
False,
self.ui.treeWidgetSubscriptions,
self.ui.treeWidgetChans
]
if currentIndex >= 0 and currentIndex < len(treeWidgetList):
return treeWidgetList[currentIndex]
else:
return False
def getAccountTreeWidget(self, account):
try:
if account.type == AccountMixin.CHAN:
return self.ui.treeWidgetChans
elif account.type == AccountMixin.SUBSCRIPTION:
return self.ui.treeWidgetSubscriptions
else:
return self.ui.treeWidgetYourIdentities
except:
return self.ui.treeWidgetYourIdentities
def getCurrentMessagelist(self):
currentIndex = self.ui.tabWidget.currentIndex();
messagelistList = [
self.ui.tableWidgetInbox,
False,
self.ui.tableWidgetInboxSubscriptions,
self.ui.tableWidgetInboxChans,
]
if currentIndex >= 0 and currentIndex < len(messagelistList):
return messagelistList[currentIndex]
else:
return False
def getAccountMessagelist(self, account):
try:
if account.type == AccountMixin.CHAN:
return self.ui.tableWidgetInboxChans
elif account.type == AccountMixin.SUBSCRIPTION:
return self.ui.tableWidgetInboxSubscriptions
else:
return self.ui.tableWidgetInbox
except:
return self.ui.tableWidgetInbox
def getCurrentMessageId(self):
messagelist = self.getCurrentMessagelist()
if messagelist:
currentRow = messagelist.currentRow()
if currentRow >= 0:
msgid = str(messagelist.item(
currentRow, 3).data(Qt.UserRole).toPyObject()) # data is saved at the 4. column of the table...
return msgid
return False
def getCurrentMessageTextedit(self):
currentIndex = self.ui.tabWidget.currentIndex();
messagelistList = [
self.ui.textEditInboxMessage,
False,
self.ui.textEditInboxMessageSubscriptions,
self.ui.textEditInboxMessageChans,
]
if currentIndex >= 0 and currentIndex < len(messagelistList):
return messagelistList[currentIndex]
else:
return False
def getAccountTextedit(self, account):
try:
if account.type == AccountMixin.CHAN:
return self.ui.textEditInboxMessageChans
elif account.type == AccountMixin.SUBSCRIPTION:
return self.ui.textEditInboxSubscriptions
else:
return self.ui.textEditInboxMessage
except:
return self.ui.textEditInboxMessage
def getCurrentSearchLine(self):
currentIndex = self.ui.tabWidget.currentIndex();
messagelistList = [
self.ui.inboxSearchLineEdit,
False,
self.ui.inboxSearchLineEditSubscriptions,
self.ui.inboxSearchLineEditChans,
]
if currentIndex >= 0 and currentIndex < len(messagelistList):
return messagelistList[currentIndex]
else:
return False
def getCurrentSearchOption(self):
currentIndex = self.ui.tabWidget.currentIndex();
messagelistList = [
self.ui.inboxSearchOption,
False,
self.ui.inboxSearchOptionSubscriptions,
self.ui.inboxSearchOptionChans,
]
if currentIndex >= 0 and currentIndex < len(messagelistList):
return messagelistList[currentIndex].currentText().toUtf8().data()
else:
return False
# Group of functions for the Your Identities dialog box # Group of functions for the Your Identities dialog box
def getCurrentItem(self, treeWidget = None):
if treeWidget is None:
treeWidget = self.getCurrentTreeWidget()
if treeWidget:
currentItem = treeWidget.currentItem()
if currentItem:
return currentItem
return False
def getCurrentAccount(self, treeWidget = None):
currentItem = self.getCurrentItem(treeWidget)
if currentItem:
account = currentItem.address
return account
else:
# TODO need debug msg?
return False
def getCurrentFolder(self, treeWidget = None):
if treeWidget is None:
treeWidget = self.getCurrentTreeWidget()
#treeWidget = self.ui.treeWidgetYourIdentities
if treeWidget:
currentItem = treeWidget.currentItem()
if currentItem and hasattr(currentItem, 'folderName'):
return currentItem.folderName
else:
return None
def setCurrentItemColor(self, color):
treeWidget = self.getCurrentTreeWidget()
if treeWidget:
brush = QtGui.QBrush()
brush.setStyle(QtCore.Qt.NoBrush)
brush.setColor(color)
currentItem = treeWidget.currentItem()
currentItem.setForeground(0, brush)
def on_action_YourIdentitiesNew(self): def on_action_YourIdentitiesNew(self):
self.click_NewAddressDialog() self.click_NewAddressDialog()
def on_action_YourIdentitiesEnable(self): def on_action_YourIdentitiesDelete(self):
currentRow = self.ui.tableWidgetYourIdentities.currentRow() account = self.getCurrentItem()
addressAtCurrentRow = str( if account.type == AccountMixin.NORMAL:
self.ui.tableWidgetYourIdentities.item(currentRow, 1).text()) return # maybe in the future
shared.config.set(addressAtCurrentRow, 'enabled', 'true') elif account.type == AccountMixin.CHAN:
shared.writeKeysFile() if QtGui.QMessageBox.question(self, "Delete channel?", _translate("MainWindow", "If you delete the channel, messages that you already received will become inaccessible. Maybe you can consider disabling the channel instead. Disabled channels will not receive new messages, but you can still view messages you already received.\n\nAre you sure you want to delete the channel?"), QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes:
self.ui.tableWidgetYourIdentities.item( shared.config.remove_section(str(account.address))
currentRow, 0).setTextColor(QApplication.palette().text().color()) else:
self.ui.tableWidgetYourIdentities.item( return
currentRow, 1).setTextColor(QApplication.palette().text().color()) else:
self.ui.tableWidgetYourIdentities.item( return
currentRow, 2).setTextColor(QApplication.palette().text().color())
if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'):
self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta
if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'):
self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(216, 119, 0)) # orange
shared.reloadMyAddressHashes()
def on_action_YourIdentitiesDisable(self):
currentRow = self.ui.tableWidgetYourIdentities.currentRow()
addressAtCurrentRow = str(
self.ui.tableWidgetYourIdentities.item(currentRow, 1).text())
shared.config.set(str(addressAtCurrentRow), 'enabled', 'false')
self.ui.tableWidgetYourIdentities.item(
currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128))
self.ui.tableWidgetYourIdentities.item(
currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128))
self.ui.tableWidgetYourIdentities.item(
currentRow, 2).setTextColor(QtGui.QColor(128, 128, 128))
if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'):
self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta
shared.writeKeysFile() shared.writeKeysFile()
shared.reloadMyAddressHashes() shared.reloadMyAddressHashes()
self.rerenderAddressBook()
if account.type == AccountMixin.NORMAL:
self.rerenderTabTreeMessages()
elif account.type == AccountMixin.CHAN:
self.rerenderTabTreeChans()
def on_action_YourIdentitiesClipboard(self): def on_action_Enable(self):
currentRow = self.ui.tableWidgetYourIdentities.currentRow() addressAtCurrentRow = self.getCurrentAccount()
addressAtCurrentRow = self.ui.tableWidgetYourIdentities.item( self.enableIdentity(addressAtCurrentRow)
currentRow, 1).text() account = self.getCurrentItem()
account.setEnabled(True)
def enableIdentity(self, address):
shared.config.set(address, 'enabled', 'true')
shared.writeKeysFile()
shared.reloadMyAddressHashes()
self.rerenderAddressBook()
def on_action_Disable(self):
address = self.getCurrentAccount()
self.disableIdentity(address)
account = self.getCurrentItem()
account.setEnabled(False)
def disableIdentity(self, address):
shared.config.set(str(address), 'enabled', 'false')
shared.writeKeysFile()
shared.reloadMyAddressHashes()
self.rerenderAddressBook()
def on_action_Clipboard(self):
address = self.getCurrentAccount()
clipboard = QtGui.QApplication.clipboard() clipboard = QtGui.QApplication.clipboard()
clipboard.setText(str(addressAtCurrentRow)) clipboard.setText(str(address))
#set avatar functions
def on_action_TreeWidgetSetAvatar(self):
address = self.getCurrentAccount()
self.setAvatar(address)
def on_action_YourIdentitiesSetAvatar(self):
self.on_action_SetAvatar(self.ui.tableWidgetYourIdentities)
def on_action_AddressBookSetAvatar(self): def on_action_AddressBookSetAvatar(self):
self.on_action_SetAvatar(self.ui.tableWidgetAddressBook) self.on_action_SetAvatar(self.ui.tableWidgetAddressBook)
def on_action_SubscriptionsSetAvatar(self):
self.on_action_SetAvatar(self.ui.tableWidgetSubscriptions)
def on_action_BlacklistSetAvatar(self): def on_action_BlacklistSetAvatar(self):
self.on_action_SetAvatar(self.ui.tableWidgetBlacklist) self.on_action_SetAvatar(self.ui.tableWidgetBlacklist)
def on_action_SetAvatar(self, thisTableWidget): def on_action_SetAvatar(self, thisTableWidget):
# thisTableWidget = self.ui.tableWidgetYourIdentities
if not os.path.exists(shared.appdata + 'avatars/'):
os.makedirs(shared.appdata + 'avatars/')
currentRow = thisTableWidget.currentRow() currentRow = thisTableWidget.currentRow()
addressAtCurrentRow = thisTableWidget.item( addressAtCurrentRow = thisTableWidget.item(
currentRow, 1).text() currentRow, 1).text()
setToIdenticon = not self.setAvatar(addressAtCurrentRow)
if setToIdenticon:
thisTableWidget.item(
currentRow, 0).setIcon(avatarize(addressAtCurrentRow))
def setAvatar(self, addressAtCurrentRow):
if not os.path.exists(shared.appdata + 'avatars/'):
os.makedirs(shared.appdata + 'avatars/')
hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow)).hexdigest() hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow)).hexdigest()
extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA']
# http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats
@ -3229,25 +3776,79 @@ more work your computer must do to send the message. A Time-To-Live of four or f
if sourcefile != '': if sourcefile != '':
copied = QtCore.QFile.copy(sourcefile, destination) copied = QtCore.QFile.copy(sourcefile, destination)
if not copied: if not copied:
print 'couldn\'t copy :(' logger.error('couldn\'t copy :(')
return False
# set the icon # set the icon
thisTableWidget.item( self.rerenderTabTreeMessages()
currentRow, 0).setIcon(avatarize(addressAtCurrentRow)) self.rerenderTabTreeSubscriptions()
self.rerenderSubscriptions() self.rerenderTabTreeChans()
self.rerenderComboBoxSendFrom() self.rerenderComboBoxSendFrom()
self.rerenderComboBoxSendFromBroadcast()
self.rerenderInboxFromLabels() self.rerenderInboxFromLabels()
self.rerenderInboxToLabels() self.rerenderInboxToLabels()
self.rerenderSentFromLabels() self.rerenderSentFromLabels()
self.rerenderSentToLabels() self.rerenderSentToLabels()
self.rerenderBlackWhiteList() self.rerenderBlackWhiteList()
# generate identicon
return False
return True
def on_context_menuYourIdentities(self, point): def on_context_menuYourIdentities(self, point):
self.popMenuYourIdentities = QtGui.QMenu(self)
self.popMenuYourIdentities.addAction(self.actionNewYourIdentities)
self.popMenuYourIdentities.addSeparator()
self.popMenuYourIdentities.addAction(self.actionClipboardYourIdentities)
self.popMenuYourIdentities.addSeparator()
if self.getCurrentItem().isEnabled:
self.popMenuYourIdentities.addAction(self.actionDisableYourIdentities)
else:
self.popMenuYourIdentities.addAction(self.actionEnableYourIdentities)
self.popMenuYourIdentities.addAction(self.actionSetAvatarYourIdentities)
self.popMenuYourIdentities.addAction(self.actionSpecialAddressBehaviorYourIdentities)
self.popMenuYourIdentities.addAction(self.actionEmailGateway)
self.popMenuYourIdentities.exec_(
self.ui.treeWidgetYourIdentities.mapToGlobal(point))
# TODO make one popMenu
def on_context_menuChan(self, point):
self.popMenu = QtGui.QMenu(self)
self.popMenu.addAction(self.actionNew)
self.popMenu.addAction(self.actionDelete)
self.popMenu.addSeparator()
self.popMenu.addAction(self.actionClipboard)
self.popMenu.addSeparator()
if self.getCurrentItem().isEnabled:
self.popMenu.addAction(self.actionDisable)
else:
self.popMenu.addAction(self.actionEnable)
self.popMenu.addAction(self.actionSetAvatar)
self.popMenu.exec_( self.popMenu.exec_(
self.ui.tableWidgetYourIdentities.mapToGlobal(point)) self.ui.treeWidgetChans.mapToGlobal(point))
def on_context_menuInbox(self, point): def on_context_menuInbox(self, point):
self.popMenuInbox.exec_(self.ui.tableWidgetInbox.mapToGlobal(point)) tableWidget = self.getCurrentMessagelist()
if tableWidget:
currentFolder = self.getCurrentFolder()
if currentFolder is None:
pass
if currentFolder == 'sent':
self.on_context_menuSent(point)
else:
self.popMenuInbox = QtGui.QMenu(self)
self.popMenuInbox.addAction(self.actionForceHtml)
self.popMenuInbox.addAction(self.actionMarkUnread)
self.popMenuInbox.addSeparator()
self.popMenuInbox.addAction(self.actionReply)
self.popMenuInbox.addAction(self.actionAddSenderToAddressBook)
self.popMenuInbox.addSeparator()
self.popMenuInbox.addAction(self.actionAddSenderToBlackList)
self.popMenuInbox.addSeparator()
self.popMenuInbox.addAction(self.actionSaveMessageAs)
if currentFolder == "trash":
self.popMenuInbox.addAction(self.actionUndeleteTrashedMessage)
else:
self.popMenuInbox.addAction(self.actionTrashInboxMessage)
self.popMenuInbox.exec_(tableWidget.mapToGlobal(point))
def on_context_menuSent(self, point): def on_context_menuSent(self, point):
self.popMenuSent = QtGui.QMenu(self) self.popMenuSent = QtGui.QMenu(self)
@ -3256,105 +3857,133 @@ more work your computer must do to send the message. A Time-To-Live of four or f
# Check to see if this item is toodifficult and display an additional # Check to see if this item is toodifficult and display an additional
# menu option (Force Send) if it is. # menu option (Force Send) if it is.
currentRow = self.ui.tableWidgetSent.currentRow()
ackData = str(self.ui.tableWidgetSent.item(
currentRow, 3).data(Qt.UserRole).toPyObject())
queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData)
for row in queryreturn:
status, = row
if status == 'toodifficult':
self.popMenuSent.addAction(self.actionForceSend)
self.popMenuSent.exec_(self.ui.tableWidgetSent.mapToGlobal(point))
def inboxSearchLineEditPressed(self):
searchKeyword = self.ui.inboxSearchLineEdit.text().toUtf8().data()
searchOption = self.ui.inboxSearchOptionCB.currentText().toUtf8().data()
self.ui.inboxSearchLineEdit.setText(QString(""))
self.ui.textEditInboxMessage.setPlainText(QString(""))
self.loadInbox(searchOption, searchKeyword)
def sentSearchLineEditPressed(self):
searchKeyword = self.ui.sentSearchLineEdit.text().toUtf8().data()
searchOption = self.ui.sentSearchOptionCB.currentText().toUtf8().data()
self.ui.sentSearchLineEdit.setText(QString(""))
self.ui.textEditInboxMessage.setPlainText(QString(""))
self.loadSent(searchOption, searchKeyword)
def tableWidgetInboxItemClicked(self):
currentRow = self.ui.tableWidgetInbox.currentRow() currentRow = self.ui.tableWidgetInbox.currentRow()
if currentRow >= 0: if currentRow >= 0:
font = QFont() ackData = str(self.ui.tableWidgetInbox.item(
font.setBold(False)
self.ui.textEditInboxMessage.setCurrentFont(font)
fromAddress = str(self.ui.tableWidgetInbox.item(
currentRow, 1).data(Qt.UserRole).toPyObject())
msgid = str(self.ui.tableWidgetInbox.item(
currentRow, 3).data(Qt.UserRole).toPyObject()) currentRow, 3).data(Qt.UserRole).toPyObject())
queryreturn = sqlQuery( queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData)
'''select message from inbox where msgid=?''', msgid) for row in queryreturn:
if queryreturn != []: status, = row
for row in queryreturn: if status == 'toodifficult':
messageText, = row self.popMenuSent.addAction(self.actionForceSend)
messageText = shared.fixPotentiallyInvalidUTF8Data(messageText)
messageText = unicode(messageText, 'utf-8)') self.popMenuSent.exec_(self.ui.tableWidgetInbox.mapToGlobal(point))
if len(messageText) > 30000:
messageText = ( def inboxSearchLineEditPressed(self):
messageText[:30000] + '\n' + searchLine = self.getCurrentSearchLine()
'--- Display of the remainder of the message ' + searchOption = self.getCurrentSearchOption()
'truncated because it is too long.\n' + if searchLine:
'--- To see the full message, right-click in the ' + searchKeyword = searchLine.text().toUtf8().data()
'Inbox view and select "View HTML code as formatted ' + searchLine.setText(QString(""))
'text",\n' + messageTextedit = self.getCurrentMessageTextedit()
'--- or select "Save message as..." to save it to a ' + if messageTextedit:
'file, or select "Reply" and ' + messageTextedit.setPlainText(QString(""))
'view the full message in the quote.') messagelist = self.getCurrentMessagelist()
# If we have received this message from either a broadcast address if messagelist:
# or from someone in our address book, display as HTML account = self.getCurrentAccount()
if decodeAddress(fromAddress)[3] in shared.broadcastSendersForWhichImWatching or shared.isAddressInMyAddressBook(fromAddress): folder = self.getCurrentFolder()
self.ui.textEditInboxMessage.setText(messageText) self.loadMessagelist(messagelist, account, folder, searchOption, searchKeyword)
def treeWidgetItemClicked(self):
messagelist = self.getCurrentMessagelist()
if messagelist:
account = self.getCurrentAccount()
folder = self.getCurrentFolder()
if folder == "new":
self.loadMessagelist(messagelist, account, None, unreadOnly = True)
else: else:
self.ui.textEditInboxMessage.setPlainText(messageText) self.loadMessagelist(messagelist, account, folder)
self.ui.tableWidgetInbox.item(currentRow, 0).setFont(font) def treeWidgetItemChanged(self, item, column):
self.ui.tableWidgetInbox.item(currentRow, 1).setFont(font) # only for manual edits. automatic edits (setText) are ignored
self.ui.tableWidgetInbox.item(currentRow, 2).setFont(font) if column != 0:
self.ui.tableWidgetInbox.item(currentRow, 3).setFont(font) return
# only account names of normal addresses (no chans/mailinglists)
if (not isinstance(item, Ui_AddressWidget)) or (not self.getCurrentTreeWidget()) or self.getCurrentTreeWidget().currentItem() is None:
return
# not visible
if (not self.getCurrentItem()) or (not isinstance (self.getCurrentItem(), Ui_AddressWidget)):
return
# only currently selected item
if item.address != self.getCurrentAccount():
return
newLabel = str(item.text(0))
if item.type == AccountMixin.SUBSCRIPTION:
oldLabel = item.label
else:
oldLabel = shared.config.get(str(item.address), 'label')
# unchanged, do not do anything either
if newLabel == oldLabel:
return
inventoryHash = str(self.ui.tableWidgetInbox.item( # recursion prevention
currentRow, 3).data(Qt.UserRole).toPyObject()) if self.recurDepth > 0:
self.ubuntuMessagingMenuClear(inventoryHash) return
sqlExecute('''update inbox set read=1 WHERE msgid=?''', inventoryHash)
self.changedInboxUnread()
def tableWidgetSentItemClicked(self): self.recurDepth += 1
currentRow = self.ui.tableWidgetSent.currentRow() item.setData(0, QtCore.Qt.EditRole, newLabel)
if currentRow >= 0: item.updateText()
ackdata = str(self.ui.tableWidgetSent.item( if item.type == AccountMixin.MAILINGLIST:
currentRow, 3).data(Qt.UserRole).toPyObject()) self.rerenderComboBoxSendFromBroadcast()
queryreturn = sqlQuery( elif item.type != AccountMixin.SUBSCRIPTION:
'''select message from sent where ackdata=?''', ackdata)
if queryreturn != []:
for row in queryreturn:
message, = row
else:
message = "Error occurred: could not load message from disk."
message = unicode(message, 'utf-8)')
self.ui.textEditSentMessage.setPlainText(message)
def tableWidgetYourIdentitiesItemChanged(self):
currentRow = self.ui.tableWidgetYourIdentities.currentRow()
if currentRow >= 0:
addressAtCurrentRow = self.ui.tableWidgetYourIdentities.item(
currentRow, 1).text()
shared.config.set(str(addressAtCurrentRow), 'label', str(
self.ui.tableWidgetYourIdentities.item(currentRow, 0).text().toUtf8()))
shared.writeKeysFile()
self.rerenderComboBoxSendFrom() self.rerenderComboBoxSendFrom()
# self.rerenderInboxFromLabels() self.recurDepth -= 1
self.rerenderInboxToLabels()
self.rerenderSentFromLabels() def tableWidgetInboxItemClicked(self):
# self.rerenderSentToLabels() folder = self.getCurrentFolder()
messageTextedit = self.getCurrentMessageTextedit()
if not messageTextedit:
return
queryreturn = []
message = ""
if folder == 'sent':
ackdata = self.getCurrentMessageId()
if ackdata and messageTextedit:
queryreturn = sqlQuery(
'''select message, 1 from sent where ackdata=?''', ackdata)
else:
msgid = self.getCurrentMessageId()
if msgid and messageTextedit:
queryreturn = sqlQuery(
'''select message, read from inbox where msgid=?''', msgid)
if queryreturn != []:
refresh = False
for row in queryreturn:
message, read = row
if folder != 'sent' and read == 0:
markread = sqlQuery(
'''UPDATE inbox SET read = 1 WHERE msgid = ?''', msgid)
refresh = True
if refresh:
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
font = QFont()
font.setBold(False)
# inventoryHashesToMarkRead = []
currentRow = tableWidget.currentRow()
# inventoryHashToMarkRead = str(tableWidget.item(
# currentRow, 3).data(Qt.UserRole).toPyObject())
# inventoryHashesToMarkRead.append(inventoryHashToMarkRead)
tableWidget.item(currentRow, 0).setFont(font)
tableWidget.item(currentRow, 0).setTextColor(AccountColor(str(tableWidget.item(currentRow, 0).data(Qt.UserRole).toPyObject())).accountColor())
tableWidget.item(currentRow, 1).setFont(font)
tableWidget.item(currentRow, 1).setTextColor(AccountColor(str(tableWidget.item(currentRow, 1).data(Qt.UserRole).toPyObject())).accountColor())
tableWidget.item(currentRow, 2).setFont(font)
tableWidget.item(currentRow, 3).setFont(font)
self.propagateUnreadCount(str(tableWidget.item(currentRow, 1 if tableWidget == self.ui.tableWidgetInboxSubscriptions else 0).data(Qt.UserRole).toPyObject()), folder, self.getCurrentTreeWidget(), -1)
else:
data = self.getCurrentMessageId()
if data != False:
message = "Error occurred: could not load message from disk."
message = unicode(message, 'utf-8)')
messageTextedit.setCurrentFont(QtGui.QFont())
messageTextedit.setTextColor(QtGui.QColor())
messageTextedit.setPlainText(message)
def tableWidgetAddressBookItemChanged(self): def tableWidgetAddressBookItemChanged(self):
currentRow = self.ui.tableWidgetAddressBook.currentRow() currentRow = self.ui.tableWidgetAddressBook.currentRow()
@ -3364,47 +3993,34 @@ more work your computer must do to send the message. A Time-To-Live of four or f
sqlExecute('''UPDATE addressbook set label=? WHERE address=?''', sqlExecute('''UPDATE addressbook set label=? WHERE address=?''',
str(self.ui.tableWidgetAddressBook.item(currentRow, 0).text().toUtf8()), str(self.ui.tableWidgetAddressBook.item(currentRow, 0).text().toUtf8()),
str(addressAtCurrentRow)) str(addressAtCurrentRow))
self.rerenderInboxFromLabels() self.ui.tableWidgetAddressBook.item(currentRow, 0).setLabel(str(self.ui.tableWidgetAddressBook.item(currentRow, 0).text().toUtf8()))
self.rerenderSentToLabels()
def tableWidgetSubscriptionsItemChanged(self):
currentRow = self.ui.tableWidgetSubscriptions.currentRow()
if currentRow >= 0:
addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
currentRow, 1).text()
sqlExecute('''UPDATE subscriptions set label=? WHERE address=?''',
str(self.ui.tableWidgetSubscriptions.item(currentRow, 0).text().toUtf8()),
str(addressAtCurrentRow))
self.rerenderInboxFromLabels() self.rerenderInboxFromLabels()
self.rerenderSentToLabels() self.rerenderSentToLabels()
def writeNewAddressToTable(self, label, address, streamNumber): def writeNewAddressToTable(self, label, address, streamNumber):
self.ui.tableWidgetYourIdentities.setSortingEnabled(False) self.rerenderTabTreeMessages()
self.ui.tableWidgetYourIdentities.insertRow(0) self.rerenderTabTreeSubscriptions()
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) self.rerenderTabTreeChans()
newItem.setIcon(avatarize(address))
self.ui.tableWidgetYourIdentities.setItem(
0, 0, newItem)
newItem = QtGui.QTableWidgetItem(address)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if shared.safeConfigGetBoolean(address, 'chan'):
newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange
self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem)
newItem = QtGui.QTableWidgetItem(streamNumber)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.ui.tableWidgetYourIdentities.setItem(0, 2, newItem)
# self.ui.tableWidgetYourIdentities.setSortingEnabled(True)
self.rerenderComboBoxSendFrom() self.rerenderComboBoxSendFrom()
self.rerenderComboBoxSendFromBroadcast()
def updateStatusBar(self, data): def updateStatusBar(self, data):
if data != "": if data != "":
with shared.printLock: logger.info('Status bar: ' + data)
print 'Status bar:', data
self.statusBar().showMessage(data) self.statusBar().showMessage(data)
def initSettings(self):
QtCore.QCoreApplication.setOrganizationName("PyBitmessage")
QtCore.QCoreApplication.setOrganizationDomain("bitmessage.org")
QtCore.QCoreApplication.setApplicationName("pybitmessageqt")
self.loadSettings()
for attr, obj in self.ui.__dict__.iteritems():
if hasattr(obj, "__class__") and isinstance(obj, settingsmixin.SettingsMixin):
loadMethod = getattr(obj, "loadSettings", None)
if callable (loadMethod):
obj.loadSettings()
class helpDialog(QtGui.QDialog): class helpDialog(QtGui.QDialog):
@ -3494,6 +4110,8 @@ class settingsDialog(QtGui.QDialog):
# On the Network settings tab: # On the Network settings tab:
self.ui.lineEditTCPPort.setText(str( self.ui.lineEditTCPPort.setText(str(
shared.config.get('bitmessagesettings', 'port'))) shared.config.get('bitmessagesettings', 'port')))
self.ui.checkBoxUPnP.setChecked(
shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'))
self.ui.checkBoxAuthentication.setChecked(shared.config.getboolean( self.ui.checkBoxAuthentication.setChecked(shared.config.getboolean(
'bitmessagesettings', 'socksauthentication')) 'bitmessagesettings', 'socksauthentication'))
self.ui.checkBoxSocksListen.setChecked(shared.config.getboolean( self.ui.checkBoxSocksListen.setChecked(shared.config.getboolean(
@ -3540,6 +4158,16 @@ class settingsDialog(QtGui.QDialog):
self.ui.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(shared.config.getint( self.ui.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(shared.config.getint(
'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')) / shared.networkDefaultPayloadLengthExtraBytes))) 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')) / shared.networkDefaultPayloadLengthExtraBytes)))
# OpenCL
if openclpow.has_opencl():
self.ui.checkBoxOpenCL.setEnabled(True)
else:
self.ui.checkBoxOpenCL.setEnabled(False)
if shared.safeConfigGetBoolean("bitmessagesettings", "opencl"):
self.ui.checkBoxOpenCL.setChecked(True)
else:
self.ui.checkBoxOpenCL.setChecked(False)
# Namecoin integration tab # Namecoin integration tab
nmctype = shared.config.get('bitmessagesettings', 'namecoinrpctype') nmctype = shared.config.get('bitmessagesettings', 'namecoinrpctype')
self.ui.lineEditNamecoinHost.setText(str( self.ui.lineEditNamecoinHost.setText(str(
@ -3666,9 +4294,7 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
self.ui = Ui_SpecialAddressBehaviorDialog() self.ui = Ui_SpecialAddressBehaviorDialog()
self.ui.setupUi(self) self.ui.setupUi(self)
self.parent = parent self.parent = parent
currentRow = parent.ui.tableWidgetYourIdentities.currentRow() addressAtCurrentRow = parent.getCurrentAccount()
addressAtCurrentRow = str(
parent.ui.tableWidgetYourIdentities.item(currentRow, 1).text())
if not shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): if not shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'):
if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'):
self.ui.radioButtonBehaviorMailingList.click() self.ui.radioButtonBehaviorMailingList.click()
@ -3688,6 +4314,25 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
class EmailGatewayDialog(QtGui.QDialog):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_EmailGatewayDialog()
self.ui.setupUi(self)
self.parent = parent
addressAtCurrentRow = parent.getCurrentAccount()
acct = accountClass(addressAtCurrentRow)
if isinstance(acct, GatewayAccount):
self.ui.radioButtonUnregister.setEnabled(True)
else:
self.ui.radioButtonUnregister.setEnabled(False)
label = shared.config.get(addressAtCurrentRow, 'label')
if label.find("@mailchuck.com") > -1:
self.ui.lineEditEmail.setText(label)
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
class AddAddressDialog(QtGui.QDialog): class AddAddressDialog(QtGui.QDialog):
@ -3799,10 +4444,10 @@ class NewAddressDialog(QtGui.QDialog):
row = 1 row = 1
# Let's fill out the 'existing address' combo box with addresses from # Let's fill out the 'existing address' combo box with addresses from
# the 'Your Identities' tab. # the 'Your Identities' tab.
while self.parent.ui.tableWidgetYourIdentities.item(row - 1, 1): for addressInKeysFile in getSortedAccounts():
self.ui.radioButtonExisting.click() self.ui.radioButtonExisting.click()
self.ui.comboBoxExisting.addItem( self.ui.comboBoxExisting.addItem(
self.parent.ui.tableWidgetYourIdentities.item(row - 1, 1).text()) addressInKeysFile)
row += 1 row += 1
self.ui.groupBoxDeterministic.setHidden(True) self.ui.groupBoxDeterministic.setHidden(True)
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
@ -3894,6 +4539,8 @@ class UISignaler(QThread):
self.emit(SIGNAL("rerenderBlackWhiteList()")) self.emit(SIGNAL("rerenderBlackWhiteList()"))
elif command == 'removeInboxRowByMsgid': elif command == 'removeInboxRowByMsgid':
self.emit(SIGNAL("removeInboxRowByMsgid(PyQt_PyObject)"), data) self.emit(SIGNAL("removeInboxRowByMsgid(PyQt_PyObject)"), data)
elif command == 'newVersionAvailable':
self.emit(SIGNAL("newVersionAvailable(PyQt_PyObject)"), data)
elif command == 'alert': elif command == 'alert':
title, text, exitAfterUserClicksOk = data title, text, exitAfterUserClicksOk = data
self.emit(SIGNAL("displayAlert(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"), title, text, exitAfterUserClicksOk) self.emit(SIGNAL("displayAlert(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"), title, text, exitAfterUserClicksOk)
@ -3901,18 +4548,88 @@ class UISignaler(QThread):
sys.stderr.write( sys.stderr.write(
'Command sent to UISignaler not recognized: %s\n' % command) 'Command sent to UISignaler not recognized: %s\n' % command)
app = None
myapp = None
class MySingleApplication(QApplication):
"""
Listener to allow our Qt form to get focus when another instance of the
application is open.
Based off this nice reimplmentation of MySingleApplication:
http://stackoverflow.com/a/12712362/2679626
"""
# Unique identifier for this application
uuid = '6ec0149b-96e1-4be1-93ab-1465fb3ebf7c'
def __init__(self, *argv):
super(MySingleApplication, self).__init__(*argv)
id = MySingleApplication.uuid
self.server = None
self.is_running = False
socket = QLocalSocket()
socket.connectToServer(id)
self.is_running = socket.waitForConnected()
# Cleanup past crashed servers
if not self.is_running:
if socket.error() == QLocalSocket.ConnectionRefusedError:
socket.disconnectFromServer()
QLocalServer.removeServer(id)
socket.abort()
# Checks if there's an instance of the local server id running
if self.is_running:
# This should be ignored, singleton.py will take care of exiting me.
pass
else:
# Nope, create a local server with this id and assign on_new_connection
# for whenever a second instance tries to run focus the application.
self.server = QLocalServer()
self.server.listen(id)
self.server.newConnection.connect(self.on_new_connection)
def __del__(self):
if self.server:
self.server.close()
def on_new_connection(self):
global myapp
if myapp:
myapp.appIndicatorShow()
def init():
global app
if not app:
app = MySingleApplication(sys.argv)
return app
def run(): def run():
app = QtGui.QApplication(sys.argv) global myapp
app = init()
change_translation(l10n.getTranslationLanguage()) change_translation(l10n.getTranslationLanguage())
app.setStyleSheet("QStatusBar::item { border: 0px solid black }") app.setStyleSheet("QStatusBar::item { border: 0px solid black }")
myapp = MyForm() myapp = MyForm()
if not shared.config.getboolean('bitmessagesettings', 'startintray'):
myapp.show()
myapp.appIndicatorInit(app) myapp.appIndicatorInit(app)
myapp.ubuntuMessagingMenuInit() myapp.ubuntuMessagingMenuInit()
myapp.notifierInit() myapp.notifierInit()
if shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): if shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
myapp.showConnectDialog() # ask the user if we may connect myapp.showConnectDialog() # ask the user if we may connect
# try:
# if shared.config.get('bitmessagesettings', 'mailchuck') < 1:
# myapp.showMigrationWizard(shared.config.get('bitmessagesettings', 'mailchuck'))
# except:
# myapp.showMigrationWizard(0)
# only show after wizards and connect dialogs have completed
if not shared.config.getboolean('bitmessagesettings', 'startintray'):
myapp.show()
sys.exit(app.exec_()) sys.exit(app.exec_())

222
src/bitmessageqt/account.py Normal file
View File

@ -0,0 +1,222 @@
from PyQt4 import QtCore, QtGui
import shared
import re
import sys
import inspect
from helper_sql import *
from addresses import decodeAddress
from foldertree import AccountMixin
from pyelliptic.openssl import OpenSSL
from utils import str_broadcast_subscribers
import time
def getSortedAccounts():
configSections = filter(lambda x: x != 'bitmessagesettings', shared.config.sections())
configSections.sort(cmp =
lambda x,y: cmp(unicode(shared.config.get(x, 'label'), 'utf-8').lower(), unicode(shared.config.get(y, 'label'), 'utf-8').lower())
)
return configSections
def getSortedSubscriptions(count = False):
queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions ORDER BY label COLLATE NOCASE ASC')
ret = {}
for row in queryreturn:
label, address, enabled = row
ret[address] = {}
ret[address]["inbox"] = {}
ret[address]["inbox"]['label'] = label
ret[address]["inbox"]['enabled'] = enabled
ret[address]["inbox"]['count'] = 0
if count:
queryreturn = sqlQuery('''SELECT fromaddress, folder, count(msgid) as cnt
FROM inbox, subscriptions
WHERE read = 0 AND subscriptions.address = inbox.fromaddress
GROUP BY inbox.fromaddress, folder''')
for row in queryreturn:
address, folder, cnt = row
ret[address][folder]['count'] = cnt
return ret
def accountClass(address):
if not shared.config.has_section(address):
if address == str_broadcast_subscribers:
subscription = BroadcastAccount(address)
if subscription.type != AccountMixin.BROADCAST:
return None
else:
subscription = SubscriptionAccount(address)
if subscription.type != AccountMixin.SUBSCRIPTION:
return None
return subscription
try:
gateway = shared.config.get(address, "gateway")
for name, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass):
# obj = g(address)
if issubclass(cls, GatewayAccount) and cls.gatewayName == gateway:
return cls(address)
# general gateway
return GatewayAccount(address)
except:
pass
# no gateway
return BMAccount(address)
class AccountColor(AccountMixin):
def __init__(self, address, type = None):
self.isEnabled = True
self.address = address
if type is None:
if address is None:
self.type = AccountMixin.ALL
elif shared.safeConfigGetBoolean(self.address, 'mailinglist'):
self.type = AccountMixin.MAILINGLIST
elif shared.safeConfigGetBoolean(self.address, 'chan'):
self.type = AccountMixin.CHAN
elif sqlQuery(
'''select label from subscriptions where address=?''', self.address):
self.type = AccountMixin.SUBSCRIPTION
else:
self.type = AccountMixin.NORMAL
else:
self.type = type
class BMAccount(object):
def __init__(self, address = None):
self.address = address
self.type = AccountMixin.NORMAL
if shared.config.has_section(address):
if shared.safeConfigGetBoolean(self.address, 'chan'):
self.type = AccountMixin.CHAN
elif shared.safeConfigGetBoolean(self.address, 'mailinglist'):
self.type = AccountMixin.MAILINGLIST
elif self.address == str_broadcast_subscribers:
self.type = AccountMixin.BROADCAST
else:
queryreturn = sqlQuery(
'''select label from subscriptions where address=?''', self.address)
if queryreturn:
self.type = AccountMixin.SUBSCRIPTION
def getLabel(self, address = None):
if address is None:
address = self.address
label = address
if shared.config.has_section(address):
label = shared.config.get(address, 'label')
queryreturn = sqlQuery(
'''select label from addressbook where address=?''', address)
if queryreturn != []:
for row in queryreturn:
label, = row
else:
queryreturn = sqlQuery(
'''select label from subscriptions where address=?''', address)
if queryreturn != []:
for row in queryreturn:
label, = row
return label
def parseMessage(self, toAddress, fromAddress, subject, message):
self.toAddress = toAddress
self.fromAddress = fromAddress
self.subject = subject
self.message = message
self.fromLabel = self.getLabel(fromAddress)
self.toLabel = self.getLabel(toAddress)
class SubscriptionAccount(BMAccount):
pass
class BroadcastAccount(BMAccount):
pass
class GatewayAccount(BMAccount):
gatewayName = None
def __init__(self, address):
super(BMAccount, self).__init__(address)
def send(self):
status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.toAddress)
ackdata = OpenSSL.rand(32)
t = ()
sqlExecute(
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
'',
self.toAddress,
ripe,
self.fromAddress,
self.subject,
self.message,
ackdata,
int(time.time()), # sentTime (this will never change)
int(time.time()), # lastActionTime
0, # sleepTill time. This will get set when the POW gets done.
'msgqueued',
0, # retryNumber
'sent', # folder
2, # encodingtype
shared.config.getint('bitmessagesettings', 'ttl')
)
shared.workerQueue.put(('sendmessage', self.toAddress))
def parseMessage(self, toAddress, fromAddress, subject, message):
super(BMAccount, self).parseMessage(toAddress, fromAddress, subject, message)
class MailchuckAccount(GatewayAccount):
# set "gateway" in keys.dat to this
gatewayName = "mailchuck"
registrationAddress = "BM-2cVYYrhaY5Gbi3KqrX9Eae2NRNrkfrhCSA"
unregistrationAddress = "BM-2cVMAHTRjZHCTPMue75XBK5Tco175DtJ9J"
relayAddress = "BM-2cWim8aZwUNqxzjMxstnUMtVEUQJeezstf"
regExpIncoming = re.compile("(.*)MAILCHUCK-FROM::(\S+) \| (.*)")
regExpOutgoing = re.compile("(\S+) (.*)")
def __init__(self, address):
super(GatewayAccount, self).__init__(address)
def createMessage(self, toAddress, fromAddress, subject, message):
self.subject = toAddress + " " + subject
self.toAddress = self.relayAddress
self.fromAddress = fromAddress
self.message = message
def register(self, email):
self.toAddress = self.registrationAddress
self.subject = email
self.message = ""
self.fromAddress = self.address
self.send()
def unregister(self):
self.toAddress = self.unregistrationAddress
self.subject = ""
self.message = ""
self.fromAddress = self.address
self.send()
def parseMessage(self, toAddress, fromAddress, subject, message):
super(GatewayAccount, self).parseMessage(toAddress, fromAddress, subject, message)
if fromAddress == self.relayAddress:
matches = self.regExpIncoming.search(subject)
if not matches is None:
self.subject = ""
if not matches.group(1) is None:
self.subject += matches.group(1)
if not matches.group(3) is None:
self.subject += matches.group(3)
if not matches.group(2) is None:
self.fromLabel = matches.group(2)
self.fromAddress = matches.group(2)
if toAddress == self.relayAddress:
matches = self.regExpOutgoing.search(subject)
if not matches is None:
if not matches.group(2) is None:
self.subject = matches.group(2)
if not matches.group(1) is None:
self.toLabel = matches.group(1)
self.toAddress = matches.group(1)

View File

@ -2,12 +2,13 @@
# Form implementation generated from reading ui file 'bitmessageui.ui' # Form implementation generated from reading ui file 'bitmessageui.ui'
# #
# Created: Sun Mar 08 22:07:43 2015 # Created: Mon Mar 23 22:18:07 2015
# by: PyQt4 UI code generator 4.10.3 # by: PyQt4 UI code generator 4.10.4
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
import settingsmixin
try: try:
_fromUtf8 = QtCore.QString.fromUtf8 _fromUtf8 = QtCore.QString.fromUtf8
@ -33,9 +34,8 @@ class Ui_MainWindow(object):
MainWindow.setTabShape(QtGui.QTabWidget.Rounded) MainWindow.setTabShape(QtGui.QTabWidget.Rounded)
self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget")) self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.gridLayout = QtGui.QGridLayout(self.centralwidget) self.gridLayout_10 = QtGui.QGridLayout(self.centralwidget)
self.gridLayout.setMargin(0) self.gridLayout_10.setObjectName(_fromUtf8("gridLayout_10"))
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.tabWidget = QtGui.QTabWidget(self.centralwidget) self.tabWidget = QtGui.QTabWidget(self.centralwidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
@ -52,27 +52,52 @@ class Ui_MainWindow(object):
self.tabWidget.setObjectName(_fromUtf8("tabWidget")) self.tabWidget.setObjectName(_fromUtf8("tabWidget"))
self.inbox = QtGui.QWidget() self.inbox = QtGui.QWidget()
self.inbox.setObjectName(_fromUtf8("inbox")) self.inbox.setObjectName(_fromUtf8("inbox"))
self.verticalLayout_2 = QtGui.QVBoxLayout(self.inbox) self.gridLayout = QtGui.QGridLayout(self.inbox)
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.horizontalLayoutSearch = QtGui.QHBoxLayout() self.horizontalSplitter_3 = settingsmixin.SSplitter()
self.horizontalLayoutSearch.setContentsMargins(-1, 0, -1, -1) self.horizontalSplitter_3.setObjectName(_fromUtf8("horizontalSplitter_3"))
self.horizontalLayoutSearch.setObjectName(_fromUtf8("horizontalLayoutSearch")) self.verticalSplitter_12 = settingsmixin.SSplitter()
self.verticalSplitter_12.setObjectName(_fromUtf8("verticalSplitter_12"))
self.verticalSplitter_12.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetYourIdentities = settingsmixin.STreeWidget(self.inbox)
self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities"))
self.treeWidgetYourIdentities.resize(200, self.treeWidgetYourIdentities.height())
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
self.treeWidgetYourIdentities.headerItem().setIcon(0, icon1)
self.verticalSplitter_12.addWidget(self.treeWidgetYourIdentities)
self.pushButtonNewAddress = QtGui.QPushButton(self.inbox)
self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress"))
self.pushButtonNewAddress.resize(200, self.pushButtonNewAddress.height())
self.verticalSplitter_12.addWidget(self.pushButtonNewAddress)
self.verticalSplitter_12.setStretchFactor(0, 1)
self.verticalSplitter_12.setStretchFactor(1, 0)
self.verticalSplitter_12.setCollapsible(0, False)
self.verticalSplitter_12.setCollapsible(1, False)
self.verticalSplitter_12.handle(1).setEnabled(False)
self.horizontalSplitter_3.addWidget(self.verticalSplitter_12)
self.verticalSplitter_7 = settingsmixin.SSplitter()
self.verticalSplitter_7.setObjectName(_fromUtf8("verticalSplitter_7"))
self.verticalSplitter_7.setOrientation(QtCore.Qt.Vertical)
self.horizontalSplitterSearch = QtGui.QSplitter()
self.horizontalSplitterSearch.setObjectName(_fromUtf8("horizontalSplitterSearch"))
self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox) self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox)
self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit")) self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit"))
self.horizontalLayoutSearch.addWidget(self.inboxSearchLineEdit) self.horizontalSplitterSearch.addWidget(self.inboxSearchLineEdit)
self.inboxSearchOptionCB = QtGui.QComboBox(self.inbox) self.inboxSearchOption = QtGui.QComboBox(self.inbox)
self.inboxSearchOptionCB.setObjectName(_fromUtf8("inboxSearchOptionCB")) self.inboxSearchOption.setObjectName(_fromUtf8("inboxSearchOption"))
self.inboxSearchOptionCB.addItem(_fromUtf8("")) self.inboxSearchOption.addItem(_fromUtf8(""))
self.inboxSearchOptionCB.addItem(_fromUtf8("")) self.inboxSearchOption.addItem(_fromUtf8(""))
self.inboxSearchOptionCB.addItem(_fromUtf8("")) self.inboxSearchOption.addItem(_fromUtf8(""))
self.inboxSearchOptionCB.addItem(_fromUtf8("")) self.inboxSearchOption.addItem(_fromUtf8(""))
self.inboxSearchOptionCB.addItem(_fromUtf8("")) self.inboxSearchOption.addItem(_fromUtf8(""))
self.horizontalLayoutSearch.addWidget(self.inboxSearchOptionCB) self.inboxSearchOption.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
self.verticalLayout_2.addLayout(self.horizontalLayoutSearch) self.horizontalSplitterSearch.addWidget(self.inboxSearchOption)
self.splitter = QtGui.QSplitter(self.inbox) self.horizontalSplitterSearch.handle(1).setEnabled(False)
self.splitter.setOrientation(QtCore.Qt.Vertical) self.horizontalSplitterSearch.setStretchFactor(0, 1)
self.splitter.setObjectName(_fromUtf8("splitter")) self.horizontalSplitterSearch.setStretchFactor(1, 0)
self.tableWidgetInbox = QtGui.QTableWidget(self.splitter) self.verticalSplitter_7.addWidget(self.horizontalSplitterSearch)
self.tableWidgetInbox = settingsmixin.STableWidget(self.inbox)
self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.tableWidgetInbox.setAlternatingRowColors(True) self.tableWidgetInbox.setAlternatingRowColors(True)
self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
@ -97,84 +122,170 @@ class Ui_MainWindow(object):
self.tableWidgetInbox.horizontalHeader().setStretchLastSection(True) self.tableWidgetInbox.horizontalHeader().setStretchLastSection(True)
self.tableWidgetInbox.verticalHeader().setVisible(False) self.tableWidgetInbox.verticalHeader().setVisible(False)
self.tableWidgetInbox.verticalHeader().setDefaultSectionSize(26) self.tableWidgetInbox.verticalHeader().setDefaultSectionSize(26)
self.textEditInboxMessage = QtGui.QTextEdit(self.splitter) self.verticalSplitter_7.addWidget(self.tableWidgetInbox)
self.textEditInboxMessage = QtGui.QTextEdit(self.inbox)
self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500)) self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessage.setReadOnly(True) self.textEditInboxMessage.setReadOnly(True)
self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage")) self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage"))
self.verticalLayout_2.addWidget(self.splitter) self.verticalSplitter_7.addWidget(self.textEditInboxMessage)
icon1 = QtGui.QIcon() self.verticalSplitter_7.setStretchFactor(0, 0)
icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.verticalSplitter_7.setStretchFactor(1, 1)
self.tabWidget.addTab(self.inbox, icon1, _fromUtf8("")) self.verticalSplitter_7.setStretchFactor(2, 2)
self.verticalSplitter_7.setCollapsible(0, False)
self.verticalSplitter_7.setCollapsible(1, False)
self.verticalSplitter_7.setCollapsible(2, False)
self.verticalSplitter_7.handle(1).setEnabled(False)
self.horizontalSplitter_3.addWidget(self.verticalSplitter_7)
self.horizontalSplitter_3.setStretchFactor(0, 0)
self.horizontalSplitter_3.setStretchFactor(1, 1)
self.horizontalSplitter_3.setCollapsible(0, False)
self.horizontalSplitter_3.setCollapsible(1, False)
self.gridLayout.addWidget(self.horizontalSplitter_3)
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.inbox, icon2, _fromUtf8(""))
self.send = QtGui.QWidget() self.send = QtGui.QWidget()
self.send.setObjectName(_fromUtf8("send")) self.send.setObjectName(_fromUtf8("send"))
self.gridLayout_2 = QtGui.QGridLayout(self.send) self.gridLayout_7 = QtGui.QGridLayout(self.send)
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7"))
self.pushButtonLoadFromAddressBook = QtGui.QPushButton(self.send) self.horizontalSplitter = settingsmixin.SSplitter()
self.horizontalSplitter.setObjectName(_fromUtf8("horizontalSplitter"))
self.verticalSplitter_2 = settingsmixin.SSplitter()
self.verticalSplitter_2.setObjectName(_fromUtf8("verticalSplitter_2"))
self.verticalSplitter_2.setOrientation(QtCore.Qt.Vertical)
self.tableWidgetAddressBook = settingsmixin.STableWidget(self.send)
self.tableWidgetAddressBook.setAlternatingRowColors(True)
self.tableWidgetAddressBook.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetAddressBook.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetAddressBook.setObjectName(_fromUtf8("tableWidgetAddressBook"))
self.tableWidgetAddressBook.setColumnCount(2)
self.tableWidgetAddressBook.setRowCount(0)
self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height())
item = QtGui.QTableWidgetItem()
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
item.setIcon(icon3)
self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetAddressBook.setHorizontalHeaderItem(1, item)
self.tableWidgetAddressBook.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetAddressBook.horizontalHeader().setDefaultSectionSize(200)
self.tableWidgetAddressBook.horizontalHeader().setHighlightSections(False)
self.tableWidgetAddressBook.horizontalHeader().setStretchLastSection(True)
self.tableWidgetAddressBook.verticalHeader().setVisible(False)
self.verticalSplitter_2.addWidget(self.tableWidgetAddressBook)
self.pushButtonAddAddressBook = QtGui.QPushButton(self.send)
self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook"))
self.pushButtonAddAddressBook.resize(200, self.pushButtonAddAddressBook.height())
self.verticalSplitter_2.addWidget(self.pushButtonAddAddressBook)
self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send)
self.pushButtonFetchNamecoinID.resize(200, self.pushButtonFetchNamecoinID.height())
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(7) font.setPointSize(9)
self.pushButtonLoadFromAddressBook.setFont(font) self.pushButtonFetchNamecoinID.setFont(font)
self.pushButtonLoadFromAddressBook.setObjectName(_fromUtf8("pushButtonLoadFromAddressBook")) self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID"))
self.gridLayout_2.addWidget(self.pushButtonLoadFromAddressBook, 3, 2, 1, 1) self.verticalSplitter_2.addWidget(self.pushButtonFetchNamecoinID)
self.label_3 = QtGui.QLabel(self.send) self.verticalSplitter_2.setStretchFactor(0, 1)
self.verticalSplitter_2.setStretchFactor(1, 0)
self.verticalSplitter_2.setStretchFactor(2, 0)
self.verticalSplitter_2.setCollapsible(0, False)
self.verticalSplitter_2.setCollapsible(1, False)
self.verticalSplitter_2.setCollapsible(2, False)
self.verticalSplitter_2.handle(1).setEnabled(False)
self.verticalSplitter_2.handle(2).setEnabled(False)
self.horizontalSplitter.addWidget(self.verticalSplitter_2)
self.verticalSplitter = settingsmixin.SSplitter()
self.verticalSplitter.setObjectName(_fromUtf8("verticalSplitter"))
self.verticalSplitter.setOrientation(QtCore.Qt.Vertical)
self.tabWidgetSend = QtGui.QTabWidget(self.send)
self.tabWidgetSend.setObjectName(_fromUtf8("tabWidgetSend"))
self.sendDirect = QtGui.QWidget()
self.sendDirect.setObjectName(_fromUtf8("sendDirect"))
self.gridLayout_8 = QtGui.QGridLayout(self.sendDirect)
self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8"))
self.verticalSplitter_5 = settingsmixin.SSplitter()
self.verticalSplitter_5.setObjectName(_fromUtf8("verticalSplitter_5"))
self.verticalSplitter_5.setOrientation(QtCore.Qt.Vertical)
self.gridLayout_2 = QtGui.QGridLayout()
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
self.label_3 = QtGui.QLabel(self.sendDirect)
self.label_3.setObjectName(_fromUtf8("label_3")) self.label_3.setObjectName(_fromUtf8("label_3"))
self.gridLayout_2.addWidget(self.label_3, 4, 0, 1, 1) self.gridLayout_2.addWidget(self.label_3, 2, 0, 1, 1)
self.pushButtonSend = QtGui.QPushButton(self.send) self.label_2 = QtGui.QLabel(self.sendDirect)
self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend")) self.label_2.setObjectName(_fromUtf8("label_2"))
self.gridLayout_2.addWidget(self.pushButtonSend, 7, 8, 1, 1) self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
self.horizontalSliderTTL = QtGui.QSlider(self.send) self.lineEditSubject = QtGui.QLineEdit(self.sendDirect)
self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(35, 0)) self.lineEditSubject.setText(_fromUtf8(""))
self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(70, 16777215)) self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject"))
self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal) self.gridLayout_2.addWidget(self.lineEditSubject, 2, 1, 1, 1)
self.horizontalSliderTTL.setInvertedAppearance(False) self.label = QtGui.QLabel(self.sendDirect)
self.horizontalSliderTTL.setInvertedControls(False) self.label.setObjectName(_fromUtf8("label"))
self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL")) self.gridLayout_2.addWidget(self.label, 1, 0, 1, 1)
self.gridLayout_2.addWidget(self.horizontalSliderTTL, 7, 6, 1, 1) self.comboBoxSendFrom = QtGui.QComboBox(self.sendDirect)
spacerItem = QtGui.QSpacerItem(20, 297, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_2.addItem(spacerItem, 6, 0, 1, 1)
self.comboBoxSendFrom = QtGui.QComboBox(self.send)
self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0)) self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0))
self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom")) self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom"))
self.gridLayout_2.addWidget(self.comboBoxSendFrom, 2, 1, 1, 1) self.gridLayout_2.addWidget(self.comboBoxSendFrom, 0, 1, 1, 1)
self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send) self.lineEditTo = QtGui.QLineEdit(self.sendDirect)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred) self.lineEditTo.setObjectName(_fromUtf8("lineEditTo"))
sizePolicy.setHorizontalStretch(0) self.gridLayout_2.addWidget(self.lineEditTo, 1, 1, 1, 1)
sizePolicy.setVerticalStretch(0) self.gridLayout_2_Widget = QtGui.QWidget()
sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth()) self.gridLayout_2_Widget.setLayout(self.gridLayout_2)
self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy) self.verticalSplitter_5.addWidget(self.gridLayout_2_Widget)
self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0)) self.textEditMessage = QtGui.QTextEdit(self.sendDirect)
self.labelHumanFriendlyTTLDescription.setMaximumSize(QtCore.QSize(45, 16777215)) self.textEditMessage.setObjectName(_fromUtf8("textEditMessage"))
self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription")) self.verticalSplitter_5.addWidget(self.textEditMessage)
self.gridLayout_2.addWidget(self.labelHumanFriendlyTTLDescription, 7, 7, 1, 1) self.verticalSplitter_5.setStretchFactor(0, 0)
self.label_4 = QtGui.QLabel(self.send) self.verticalSplitter_5.setStretchFactor(1, 1)
self.label_4.setObjectName(_fromUtf8("label_4")) self.verticalSplitter_5.setCollapsible(0, False)
self.gridLayout_2.addWidget(self.label_4, 5, 0, 1, 1) self.verticalSplitter_5.setCollapsible(1, False)
self.label = QtGui.QLabel(self.send) self.verticalSplitter_5.handle(1).setEnabled(False)
self.label.setObjectName(_fromUtf8("label")) self.gridLayout_8.addWidget(self.verticalSplitter_5, 0, 0, 1, 1)
self.gridLayout_2.addWidget(self.label, 3, 0, 1, 1) self.tabWidgetSend.addTab(self.sendDirect, _fromUtf8(""))
self.radioButtonSpecific = QtGui.QRadioButton(self.send) self.sendBroadcast = QtGui.QWidget()
self.radioButtonSpecific.setChecked(True) self.sendBroadcast.setObjectName(_fromUtf8("sendBroadcast"))
self.radioButtonSpecific.setObjectName(_fromUtf8("radioButtonSpecific")) self.gridLayout_9 = QtGui.QGridLayout(self.sendBroadcast)
self.gridLayout_2.addWidget(self.radioButtonSpecific, 0, 1, 1, 1) self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9"))
self.labelSendBroadcastWarning = QtGui.QLabel(self.send) self.verticalSplitter_6 = settingsmixin.SSplitter()
self.labelSendBroadcastWarning.setEnabled(True) self.verticalSplitter_6.setObjectName(_fromUtf8("verticalSplitter_6"))
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Preferred) self.verticalSplitter_6.setOrientation(QtCore.Qt.Vertical)
sizePolicy.setHorizontalStretch(0) self.gridLayout_5 = QtGui.QGridLayout()
sizePolicy.setVerticalStretch(0) self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5"))
sizePolicy.setHeightForWidth(self.labelSendBroadcastWarning.sizePolicy().hasHeightForWidth()) self.label_8 = QtGui.QLabel(self.sendBroadcast)
self.labelSendBroadcastWarning.setSizePolicy(sizePolicy) self.label_8.setObjectName(_fromUtf8("label_8"))
self.labelSendBroadcastWarning.setIndent(-1) self.gridLayout_5.addWidget(self.label_8, 0, 0, 1, 1)
self.labelSendBroadcastWarning.setObjectName(_fromUtf8("labelSendBroadcastWarning")) self.lineEditSubjectBroadcast = QtGui.QLineEdit(self.sendBroadcast)
self.gridLayout_2.addWidget(self.labelSendBroadcastWarning, 7, 1, 1, 4) self.lineEditSubjectBroadcast.setText(_fromUtf8(""))
self.radioButtonBroadcast = QtGui.QRadioButton(self.send) self.lineEditSubjectBroadcast.setObjectName(_fromUtf8("lineEditSubjectBroadcast"))
self.radioButtonBroadcast.setObjectName(_fromUtf8("radioButtonBroadcast")) self.gridLayout_5.addWidget(self.lineEditSubjectBroadcast, 1, 1, 1, 1)
self.gridLayout_2.addWidget(self.radioButtonBroadcast, 1, 1, 1, 2) self.label_7 = QtGui.QLabel(self.sendBroadcast)
self.label_7.setObjectName(_fromUtf8("label_7"))
self.gridLayout_5.addWidget(self.label_7, 1, 0, 1, 1)
self.comboBoxSendFromBroadcast = QtGui.QComboBox(self.sendBroadcast)
self.comboBoxSendFromBroadcast.setMinimumSize(QtCore.QSize(300, 0))
self.comboBoxSendFromBroadcast.setObjectName(_fromUtf8("comboBoxSendFromBroadcast"))
self.gridLayout_5.addWidget(self.comboBoxSendFromBroadcast, 0, 1, 1, 1)
self.gridLayout_5_Widget = QtGui.QWidget()
self.gridLayout_5_Widget.setLayout(self.gridLayout_5)
self.verticalSplitter_6.addWidget(self.gridLayout_5_Widget)
self.textEditMessageBroadcast = QtGui.QTextEdit(self.sendBroadcast)
self.textEditMessageBroadcast.setObjectName(_fromUtf8("textEditMessageBroadcast"))
self.verticalSplitter_6.addWidget(self.textEditMessageBroadcast)
self.verticalSplitter_6.setStretchFactor(0, 0)
self.verticalSplitter_6.setStretchFactor(1, 1)
self.verticalSplitter_6.setCollapsible(0, False)
self.verticalSplitter_6.setCollapsible(1, False)
self.verticalSplitter_6.handle(1).setEnabled(False)
self.gridLayout_9.addWidget(self.verticalSplitter_6, 0, 0, 1, 1)
self.tabWidgetSend.addTab(self.sendBroadcast, _fromUtf8(""))
self.verticalSplitter.addWidget(self.tabWidgetSend)
self.horizontalSplitter_5 = QtGui.QSplitter()
self.horizontalSplitter_5.setObjectName(_fromUtf8("horizontalSplitter_5"))
self.pushButtonTTL = QtGui.QPushButton(self.send) self.pushButtonTTL = QtGui.QPushButton(self.send)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) # sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) # sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) # sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButtonTTL.sizePolicy().hasHeightForWidth()) # sizePolicy.setHeightForWidth(self.pushButtonTTL.sizePolicy().hasHeightForWidth())
self.pushButtonTTL.setSizePolicy(sizePolicy) # self.pushButtonTTL.setSizePolicy(sizePolicy)
self.pushButtonTTL.setMaximumSize(QtCore.QSize(32, 16777215))
palette = QtGui.QPalette() palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(0, 0, 255)) brush = QtGui.QBrush(QtGui.QColor(0, 0, 255))
brush.setStyle(QtCore.Qt.SolidPattern) brush.setStyle(QtCore.Qt.SolidPattern)
@ -191,194 +302,252 @@ class Ui_MainWindow(object):
self.pushButtonTTL.setFont(font) self.pushButtonTTL.setFont(font)
self.pushButtonTTL.setFlat(True) self.pushButtonTTL.setFlat(True)
self.pushButtonTTL.setObjectName(_fromUtf8("pushButtonTTL")) self.pushButtonTTL.setObjectName(_fromUtf8("pushButtonTTL"))
self.gridLayout_2.addWidget(self.pushButtonTTL, 7, 5, 1, 1) self.horizontalSplitter_5.addWidget(self.pushButtonTTL)
self.label_2 = QtGui.QLabel(self.send) self.horizontalSliderTTL = QtGui.QSlider(self.send)
self.label_2.setObjectName(_fromUtf8("label_2")) self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(35, 0))
self.gridLayout_2.addWidget(self.label_2, 2, 0, 1, 1) self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal)
self.lineEditTo = QtGui.QLineEdit(self.send) self.horizontalSliderTTL.setInvertedAppearance(False)
self.lineEditTo.setObjectName(_fromUtf8("lineEditTo")) self.horizontalSliderTTL.setInvertedControls(False)
self.gridLayout_2.addWidget(self.lineEditTo, 3, 1, 1, 1) self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL"))
self.textEditMessage = QtGui.QTextEdit(self.send) self.horizontalSplitter_5.addWidget(self.horizontalSliderTTL)
self.textEditMessage.setObjectName(_fromUtf8("textEditMessage")) self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send)
self.gridLayout_2.addWidget(self.textEditMessage, 5, 1, 2, 8) # sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred)
self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send) # sizePolicy.setHorizontalStretch(0)
font = QtGui.QFont() # sizePolicy.setVerticalStretch(0)
font.setPointSize(7) # sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth())
self.pushButtonFetchNamecoinID.setFont(font) # self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy)
self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID")) self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0))
self.gridLayout_2.addWidget(self.pushButtonFetchNamecoinID, 3, 3, 1, 1) self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription"))
self.labelFrom = QtGui.QLabel(self.send) self.horizontalSplitter_5.addWidget(self.labelHumanFriendlyTTLDescription)
self.labelFrom.setText(_fromUtf8("")) self.pushButtonSend = QtGui.QPushButton(self.send)
self.labelFrom.setObjectName(_fromUtf8("labelFrom")) self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend"))
self.gridLayout_2.addWidget(self.labelFrom, 2, 2, 1, 7) self.horizontalSplitter_5.addWidget(self.pushButtonSend)
self.lineEditSubject = QtGui.QLineEdit(self.send) self.pushButtonTTL.setMaximumSize(QtCore.QSize(32, self.pushButtonSend.height()))
self.lineEditSubject.setText(_fromUtf8("")) self.labelHumanFriendlyTTLDescription.setMaximumSize(QtCore.QSize(45, self.pushButtonSend.height()))
self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject")) self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(70, self.pushButtonSend.height()))
self.gridLayout_2.addWidget(self.lineEditSubject, 4, 1, 1, 8) self.horizontalSplitter_5.resize(self.horizontalSplitter_5.width(), self.pushButtonSend.height())
icon2 = QtGui.QIcon() self.horizontalSplitter_5.setStretchFactor(0, 0)
icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.horizontalSplitter_5.setStretchFactor(1, 0)
self.tabWidget.addTab(self.send, icon2, _fromUtf8("")) self.horizontalSplitter_5.setStretchFactor(2, 0)
self.sent = QtGui.QWidget() self.horizontalSplitter_5.setStretchFactor(3, 1)
self.sent.setObjectName(_fromUtf8("sent")) self.horizontalSplitter_5.setCollapsible(0, False)
self.verticalLayout = QtGui.QVBoxLayout(self.sent) self.horizontalSplitter_5.setCollapsible(1, False)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) self.horizontalSplitter_5.setCollapsible(2, False)
self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalSplitter_5.setCollapsible(3, False)
self.horizontalLayout.setContentsMargins(-1, 0, -1, -1) self.horizontalSplitter_5.handle(1).setEnabled(False)
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) self.horizontalSplitter_5.handle(2).setEnabled(False)
self.sentSearchLineEdit = QtGui.QLineEdit(self.sent) self.horizontalSplitter_5.handle(3).setEnabled(False)
self.sentSearchLineEdit.setObjectName(_fromUtf8("sentSearchLineEdit")) self.verticalSplitter.addWidget(self.horizontalSplitter_5)
self.horizontalLayout.addWidget(self.sentSearchLineEdit) self.verticalSplitter.setStretchFactor(0, 0)
self.sentSearchOptionCB = QtGui.QComboBox(self.sent) self.verticalSplitter.setStretchFactor(1, 1)
self.sentSearchOptionCB.setObjectName(_fromUtf8("sentSearchOptionCB")) self.verticalSplitter.setCollapsible(0, False)
self.sentSearchOptionCB.addItem(_fromUtf8("")) self.verticalSplitter.setCollapsible(1, False)
self.sentSearchOptionCB.addItem(_fromUtf8("")) self.verticalSplitter.handle(1).setEnabled(False)
self.sentSearchOptionCB.addItem(_fromUtf8("")) self.horizontalSplitter.addWidget(self.verticalSplitter)
self.sentSearchOptionCB.addItem(_fromUtf8("")) self.horizontalSplitter.setStretchFactor(0, 0)
self.sentSearchOptionCB.addItem(_fromUtf8("")) self.horizontalSplitter.setStretchFactor(1, 1)
self.horizontalLayout.addWidget(self.sentSearchOptionCB) self.horizontalSplitter.setCollapsible(0, False)
self.verticalLayout.addLayout(self.horizontalLayout) self.horizontalSplitter.setCollapsible(1, False)
self.splitter_2 = QtGui.QSplitter(self.sent) self.gridLayout_7.addWidget(self.horizontalSplitter, 0, 0, 1, 1)
self.splitter_2.setOrientation(QtCore.Qt.Vertical)
self.splitter_2.setObjectName(_fromUtf8("splitter_2"))
self.tableWidgetSent = QtGui.QTableWidget(self.splitter_2)
self.tableWidgetSent.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.tableWidgetSent.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.tableWidgetSent.setAlternatingRowColors(True)
self.tableWidgetSent.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetSent.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetSent.setWordWrap(False)
self.tableWidgetSent.setObjectName(_fromUtf8("tableWidgetSent"))
self.tableWidgetSent.setColumnCount(4)
self.tableWidgetSent.setRowCount(0)
item = QtGui.QTableWidgetItem()
self.tableWidgetSent.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetSent.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetSent.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetSent.setHorizontalHeaderItem(3, item)
self.tableWidgetSent.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetSent.horizontalHeader().setDefaultSectionSize(130)
self.tableWidgetSent.horizontalHeader().setHighlightSections(False)
self.tableWidgetSent.horizontalHeader().setSortIndicatorShown(False)
self.tableWidgetSent.horizontalHeader().setStretchLastSection(True)
self.tableWidgetSent.verticalHeader().setVisible(False)
self.tableWidgetSent.verticalHeader().setStretchLastSection(False)
self.textEditSentMessage = QtGui.QTextEdit(self.splitter_2)
self.textEditSentMessage.setReadOnly(True)
self.textEditSentMessage.setObjectName(_fromUtf8("textEditSentMessage"))
self.verticalLayout.addWidget(self.splitter_2)
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/sent.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.sent, icon3, _fromUtf8(""))
self.youridentities = QtGui.QWidget()
self.youridentities.setObjectName(_fromUtf8("youridentities"))
self.gridLayout_3 = QtGui.QGridLayout(self.youridentities)
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
self.pushButtonNewAddress = QtGui.QPushButton(self.youridentities)
self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress"))
self.gridLayout_3.addWidget(self.pushButtonNewAddress, 0, 0, 1, 1)
spacerItem1 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_3.addItem(spacerItem1, 0, 1, 1, 1)
self.tableWidgetYourIdentities = QtGui.QTableWidget(self.youridentities)
self.tableWidgetYourIdentities.setFrameShadow(QtGui.QFrame.Sunken)
self.tableWidgetYourIdentities.setLineWidth(1)
self.tableWidgetYourIdentities.setAlternatingRowColors(True)
self.tableWidgetYourIdentities.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
self.tableWidgetYourIdentities.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetYourIdentities.setObjectName(_fromUtf8("tableWidgetYourIdentities"))
self.tableWidgetYourIdentities.setColumnCount(3)
self.tableWidgetYourIdentities.setRowCount(0)
item = QtGui.QTableWidgetItem()
font = QtGui.QFont()
font.setKerning(True)
item.setFont(font)
self.tableWidgetYourIdentities.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetYourIdentities.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetYourIdentities.setHorizontalHeaderItem(2, item)
self.tableWidgetYourIdentities.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetYourIdentities.horizontalHeader().setDefaultSectionSize(346)
self.tableWidgetYourIdentities.horizontalHeader().setMinimumSectionSize(52)
self.tableWidgetYourIdentities.horizontalHeader().setSortIndicatorShown(True)
self.tableWidgetYourIdentities.horizontalHeader().setStretchLastSection(True)
self.tableWidgetYourIdentities.verticalHeader().setVisible(False)
self.tableWidgetYourIdentities.verticalHeader().setDefaultSectionSize(26)
self.tableWidgetYourIdentities.verticalHeader().setSortIndicatorShown(False)
self.tableWidgetYourIdentities.verticalHeader().setStretchLastSection(False)
self.gridLayout_3.addWidget(self.tableWidgetYourIdentities, 1, 0, 1, 2)
icon4 = QtGui.QIcon() icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.youridentities, icon4, _fromUtf8("")) self.tabWidget.addTab(self.send, icon4, _fromUtf8(""))
self.subscriptions = QtGui.QWidget() self.subscriptions = QtGui.QWidget()
self.subscriptions.setObjectName(_fromUtf8("subscriptions")) self.subscriptions.setObjectName(_fromUtf8("subscriptions"))
self.gridLayout_4 = QtGui.QGridLayout(self.subscriptions) self.gridLayout_3 = QtGui.QGridLayout(self.subscriptions)
self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
self.label_5 = QtGui.QLabel(self.subscriptions) self.horizontalSplitter_4 = settingsmixin.SSplitter()
self.label_5.setWordWrap(True) self.horizontalSplitter_4.setObjectName(_fromUtf8("horizontalSplitter_4"))
self.label_5.setObjectName(_fromUtf8("label_5")) self.verticalSplitter_3 = settingsmixin.SSplitter()
self.gridLayout_4.addWidget(self.label_5, 0, 0, 1, 2) self.verticalSplitter_3.setObjectName(_fromUtf8("verticalSplitter_3"))
self.verticalSplitter_3.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetSubscriptions = settingsmixin.STreeWidget(self.subscriptions)
self.treeWidgetSubscriptions.setAlternatingRowColors(True)
self.treeWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
self.treeWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.treeWidgetSubscriptions.setObjectName(_fromUtf8("treeWidgetSubscriptions"))
self.treeWidgetSubscriptions.resize(200, self.treeWidgetSubscriptions.height())
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
self.treeWidgetSubscriptions.headerItem().setIcon(0, icon5)
self.verticalSplitter_3.addWidget(self.treeWidgetSubscriptions)
self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions) self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions)
self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription")) self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription"))
self.gridLayout_4.addWidget(self.pushButtonAddSubscription, 1, 0, 1, 1) self.pushButtonAddSubscription.resize(200, self.pushButtonAddSubscription.height())
spacerItem2 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.verticalSplitter_3.addWidget(self.pushButtonAddSubscription)
self.gridLayout_4.addItem(spacerItem2, 1, 1, 1, 1) self.verticalSplitter_3.setStretchFactor(0, 1)
self.tableWidgetSubscriptions = QtGui.QTableWidget(self.subscriptions) self.verticalSplitter_3.setStretchFactor(1, 0)
self.tableWidgetSubscriptions.setAlternatingRowColors(True) self.verticalSplitter_3.setCollapsible(0, False)
self.tableWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.verticalSplitter_3.setCollapsible(1, False)
self.tableWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.verticalSplitter_3.handle(1).setEnabled(False)
self.tableWidgetSubscriptions.setObjectName(_fromUtf8("tableWidgetSubscriptions")) self.horizontalSplitter_4.addWidget(self.verticalSplitter_3)
self.tableWidgetSubscriptions.setColumnCount(2) self.verticalSplitter_4 = settingsmixin.SSplitter()
self.tableWidgetSubscriptions.setRowCount(0) self.verticalSplitter_4.setObjectName(_fromUtf8("verticalSplitter_4"))
self.verticalSplitter_4.setOrientation(QtCore.Qt.Vertical)
self.horizontalSplitter_2 = QtGui.QSplitter()
self.horizontalSplitter_2.setObjectName(_fromUtf8("horizontalSplitter_2"))
self.inboxSearchLineEditSubscriptions = QtGui.QLineEdit(self.subscriptions)
self.inboxSearchLineEditSubscriptions.setObjectName(_fromUtf8("inboxSearchLineEditSubscriptions"))
self.horizontalSplitter_2.addWidget(self.inboxSearchLineEditSubscriptions)
self.inboxSearchOptionSubscriptions = QtGui.QComboBox(self.subscriptions)
self.inboxSearchOptionSubscriptions.setObjectName(_fromUtf8("inboxSearchOptionSubscriptions"))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
self.inboxSearchOptionSubscriptions.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
self.horizontalSplitter_2.addWidget(self.inboxSearchOptionSubscriptions)
self.horizontalSplitter_2.handle(1).setEnabled(False)
self.horizontalSplitter_2.setStretchFactor(0, 1)
self.horizontalSplitter_2.setStretchFactor(1, 0)
self.verticalSplitter_4.addWidget(self.horizontalSplitter_2)
self.tableWidgetInboxSubscriptions = settingsmixin.STableWidget(self.subscriptions)
self.tableWidgetInboxSubscriptions.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.tableWidgetInboxSubscriptions.setAlternatingRowColors(True)
self.tableWidgetInboxSubscriptions.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetInboxSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetInboxSubscriptions.setWordWrap(False)
self.tableWidgetInboxSubscriptions.setObjectName(_fromUtf8("tableWidgetInboxSubscriptions"))
self.tableWidgetInboxSubscriptions.setColumnCount(4)
self.tableWidgetInboxSubscriptions.setRowCount(0)
item = QtGui.QTableWidgetItem() item = QtGui.QTableWidgetItem()
self.tableWidgetSubscriptions.setHorizontalHeaderItem(0, item) self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem() item = QtGui.QTableWidgetItem()
self.tableWidgetSubscriptions.setHorizontalHeaderItem(1, item) self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(1, item)
self.tableWidgetSubscriptions.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetSubscriptions.horizontalHeader().setDefaultSectionSize(400)
self.tableWidgetSubscriptions.horizontalHeader().setHighlightSections(False)
self.tableWidgetSubscriptions.horizontalHeader().setSortIndicatorShown(False)
self.tableWidgetSubscriptions.horizontalHeader().setStretchLastSection(True)
self.tableWidgetSubscriptions.verticalHeader().setVisible(False)
self.gridLayout_4.addWidget(self.tableWidgetSubscriptions, 2, 0, 1, 2)
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.subscriptions, icon5, _fromUtf8(""))
self.addressbook = QtGui.QWidget()
self.addressbook.setObjectName(_fromUtf8("addressbook"))
self.gridLayout_5 = QtGui.QGridLayout(self.addressbook)
self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5"))
self.label_6 = QtGui.QLabel(self.addressbook)
self.label_6.setWordWrap(True)
self.label_6.setObjectName(_fromUtf8("label_6"))
self.gridLayout_5.addWidget(self.label_6, 0, 0, 1, 2)
self.pushButtonAddAddressBook = QtGui.QPushButton(self.addressbook)
self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook"))
self.gridLayout_5.addWidget(self.pushButtonAddAddressBook, 1, 0, 1, 1)
spacerItem3 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_5.addItem(spacerItem3, 1, 1, 1, 1)
self.tableWidgetAddressBook = QtGui.QTableWidget(self.addressbook)
self.tableWidgetAddressBook.setAlternatingRowColors(True)
self.tableWidgetAddressBook.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetAddressBook.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetAddressBook.setObjectName(_fromUtf8("tableWidgetAddressBook"))
self.tableWidgetAddressBook.setColumnCount(2)
self.tableWidgetAddressBook.setRowCount(0)
item = QtGui.QTableWidgetItem() item = QtGui.QTableWidgetItem()
self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item) self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem() item = QtGui.QTableWidgetItem()
self.tableWidgetAddressBook.setHorizontalHeaderItem(1, item) self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(3, item)
self.tableWidgetAddressBook.horizontalHeader().setCascadingSectionResizes(True) self.tableWidgetInboxSubscriptions.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetAddressBook.horizontalHeader().setDefaultSectionSize(400) self.tableWidgetInboxSubscriptions.horizontalHeader().setDefaultSectionSize(200)
self.tableWidgetAddressBook.horizontalHeader().setHighlightSections(False) self.tableWidgetInboxSubscriptions.horizontalHeader().setHighlightSections(False)
self.tableWidgetAddressBook.horizontalHeader().setStretchLastSection(True) self.tableWidgetInboxSubscriptions.horizontalHeader().setMinimumSectionSize(27)
self.tableWidgetAddressBook.verticalHeader().setVisible(False) self.tableWidgetInboxSubscriptions.horizontalHeader().setSortIndicatorShown(False)
self.gridLayout_5.addWidget(self.tableWidgetAddressBook, 2, 0, 1, 2) self.tableWidgetInboxSubscriptions.horizontalHeader().setStretchLastSection(True)
self.tableWidgetInboxSubscriptions.verticalHeader().setVisible(False)
self.tableWidgetInboxSubscriptions.verticalHeader().setDefaultSectionSize(26)
self.verticalSplitter_4.addWidget(self.tableWidgetInboxSubscriptions)
self.textEditInboxMessageSubscriptions = QtGui.QTextEdit(self.subscriptions)
self.textEditInboxMessageSubscriptions.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessageSubscriptions.setReadOnly(True)
self.textEditInboxMessageSubscriptions.setObjectName(_fromUtf8("textEditInboxMessageSubscriptions"))
self.verticalSplitter_4.addWidget(self.textEditInboxMessageSubscriptions)
self.verticalSplitter_4.setStretchFactor(0, 0)
self.verticalSplitter_4.setStretchFactor(1, 1)
self.verticalSplitter_4.setStretchFactor(2, 2)
self.verticalSplitter_4.setCollapsible(0, False)
self.verticalSplitter_4.setCollapsible(1, False)
self.verticalSplitter_4.setCollapsible(2, False)
self.verticalSplitter_4.handle(1).setEnabled(False)
self.horizontalSplitter_4.addWidget(self.verticalSplitter_4)
self.horizontalSplitter_4.setStretchFactor(0, 0)
self.horizontalSplitter_4.setStretchFactor(1, 1)
self.horizontalSplitter_4.setCollapsible(0, False)
self.horizontalSplitter_4.setCollapsible(1, False)
self.gridLayout_3.addWidget(self.horizontalSplitter_4, 0, 0, 1, 1)
icon6 = QtGui.QIcon() icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.addressbook, icon6, _fromUtf8("")) self.tabWidget.addTab(self.subscriptions, icon6, _fromUtf8(""))
self.chans = QtGui.QWidget()
self.chans.setObjectName(_fromUtf8("chans"))
self.gridLayout_4 = QtGui.QGridLayout(self.chans)
self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4"))
self.horizontalSplitter_7 = settingsmixin.SSplitter()
self.horizontalSplitter_7.setObjectName(_fromUtf8("horizontalSplitter_7"))
self.verticalSplitter_17 = settingsmixin.SSplitter()
self.verticalSplitter_17.setObjectName(_fromUtf8("verticalSplitter_17"))
self.verticalSplitter_17.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetChans = settingsmixin.STreeWidget(self.chans)
self.treeWidgetChans.setFrameShadow(QtGui.QFrame.Sunken)
self.treeWidgetChans.setLineWidth(1)
self.treeWidgetChans.setAlternatingRowColors(True)
self.treeWidgetChans.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
self.treeWidgetChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.treeWidgetChans.setObjectName(_fromUtf8("treeWidgetChans"))
self.treeWidgetChans.resize(200, self.treeWidgetChans.height())
icon7 = QtGui.QIcon()
icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
self.treeWidgetChans.headerItem().setIcon(0, icon7)
self.verticalSplitter_17.addWidget(self.treeWidgetChans)
self.pushButtonAddChan = QtGui.QPushButton(self.chans)
self.pushButtonAddChan.setObjectName(_fromUtf8("pushButtonAddChan"))
self.pushButtonAddChan.resize(200, self.pushButtonAddChan.height())
self.verticalSplitter_17.addWidget(self.pushButtonAddChan)
self.verticalSplitter_17.setStretchFactor(0, 1)
self.verticalSplitter_17.setStretchFactor(1, 0)
self.verticalSplitter_17.setCollapsible(0, False)
self.verticalSplitter_17.setCollapsible(1, False)
self.verticalSplitter_17.handle(1).setEnabled(False)
self.horizontalSplitter_7.addWidget(self.verticalSplitter_17)
self.verticalSplitter_8 = settingsmixin.SSplitter()
self.verticalSplitter_8.setObjectName(_fromUtf8("verticalSplitter_8"))
self.verticalSplitter_8.setOrientation(QtCore.Qt.Vertical)
self.horizontalSplitter_6 = QtGui.QSplitter()
self.horizontalSplitter_6.setObjectName(_fromUtf8("horizontalSplitter_6"))
self.inboxSearchLineEditChans = QtGui.QLineEdit(self.chans)
self.inboxSearchLineEditChans.setObjectName(_fromUtf8("inboxSearchLineEditChans"))
self.horizontalSplitter_6.addWidget(self.inboxSearchLineEditChans)
self.inboxSearchOptionChans = QtGui.QComboBox(self.chans)
self.inboxSearchOptionChans.setObjectName(_fromUtf8("inboxSearchOptionChans"))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.addItem(_fromUtf8(""))
self.inboxSearchOptionChans.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
self.horizontalSplitter_6.addWidget(self.inboxSearchOptionChans)
self.horizontalSplitter_6.handle(1).setEnabled(False)
self.horizontalSplitter_6.setStretchFactor(0, 1)
self.horizontalSplitter_6.setStretchFactor(1, 0)
self.verticalSplitter_8.addWidget(self.horizontalSplitter_6)
self.tableWidgetInboxChans = settingsmixin.STableWidget(self.chans)
self.tableWidgetInboxChans.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.tableWidgetInboxChans.setAlternatingRowColors(True)
self.tableWidgetInboxChans.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetInboxChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetInboxChans.setWordWrap(False)
self.tableWidgetInboxChans.setObjectName(_fromUtf8("tableWidgetInboxChans"))
self.tableWidgetInboxChans.setColumnCount(4)
self.tableWidgetInboxChans.setRowCount(0)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(3, item)
self.tableWidgetInboxChans.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInboxChans.horizontalHeader().setDefaultSectionSize(200)
self.tableWidgetInboxChans.horizontalHeader().setHighlightSections(False)
self.tableWidgetInboxChans.horizontalHeader().setMinimumSectionSize(27)
self.tableWidgetInboxChans.horizontalHeader().setSortIndicatorShown(False)
self.tableWidgetInboxChans.horizontalHeader().setStretchLastSection(True)
self.tableWidgetInboxChans.verticalHeader().setVisible(False)
self.tableWidgetInboxChans.verticalHeader().setDefaultSectionSize(26)
self.verticalSplitter_8.addWidget(self.tableWidgetInboxChans)
self.textEditInboxMessageChans = QtGui.QTextEdit(self.chans)
self.textEditInboxMessageChans.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessageChans.setReadOnly(True)
self.textEditInboxMessageChans.setObjectName(_fromUtf8("textEditInboxMessageChans"))
self.verticalSplitter_8.addWidget(self.textEditInboxMessageChans)
self.verticalSplitter_8.setStretchFactor(0, 0)
self.verticalSplitter_8.setStretchFactor(1, 1)
self.verticalSplitter_8.setStretchFactor(2, 2)
self.verticalSplitter_8.setCollapsible(0, False)
self.verticalSplitter_8.setCollapsible(1, False)
self.verticalSplitter_8.setCollapsible(2, False)
self.verticalSplitter_8.handle(1).setEnabled(False)
self.horizontalSplitter_7.addWidget(self.verticalSplitter_8)
self.horizontalSplitter_7.setStretchFactor(0, 0)
self.horizontalSplitter_7.setStretchFactor(1, 1)
self.horizontalSplitter_7.setCollapsible(0, False)
self.horizontalSplitter_7.setCollapsible(1, False)
self.gridLayout_4.addWidget(self.horizontalSplitter_7, 0, 0, 1, 1)
icon8 = QtGui.QIcon()
icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.chans, icon8, _fromUtf8(""))
self.blackwhitelist = QtGui.QWidget() self.blackwhitelist = QtGui.QWidget()
self.blackwhitelist.setObjectName(_fromUtf8("blackwhitelist")) self.blackwhitelist.setObjectName(_fromUtf8("blackwhitelist"))
self.gridLayout_6 = QtGui.QGridLayout(self.blackwhitelist) self.gridLayout_6 = QtGui.QGridLayout(self.blackwhitelist)
@ -393,9 +562,9 @@ class Ui_MainWindow(object):
self.pushButtonAddBlacklist = QtGui.QPushButton(self.blackwhitelist) self.pushButtonAddBlacklist = QtGui.QPushButton(self.blackwhitelist)
self.pushButtonAddBlacklist.setObjectName(_fromUtf8("pushButtonAddBlacklist")) self.pushButtonAddBlacklist.setObjectName(_fromUtf8("pushButtonAddBlacklist"))
self.gridLayout_6.addWidget(self.pushButtonAddBlacklist, 2, 0, 1, 1) self.gridLayout_6.addWidget(self.pushButtonAddBlacklist, 2, 0, 1, 1)
spacerItem4 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) spacerItem = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_6.addItem(spacerItem4, 2, 1, 1, 1) self.gridLayout_6.addItem(spacerItem, 2, 1, 1, 1)
self.tableWidgetBlacklist = QtGui.QTableWidget(self.blackwhitelist) self.tableWidgetBlacklist = settingsmixin.STableWidget(self.blackwhitelist)
self.tableWidgetBlacklist.setAlternatingRowColors(True) self.tableWidgetBlacklist.setAlternatingRowColors(True)
self.tableWidgetBlacklist.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.tableWidgetBlacklist.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
self.tableWidgetBlacklist.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.tableWidgetBlacklist.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
@ -413,20 +582,20 @@ class Ui_MainWindow(object):
self.tableWidgetBlacklist.horizontalHeader().setStretchLastSection(True) self.tableWidgetBlacklist.horizontalHeader().setStretchLastSection(True)
self.tableWidgetBlacklist.verticalHeader().setVisible(False) self.tableWidgetBlacklist.verticalHeader().setVisible(False)
self.gridLayout_6.addWidget(self.tableWidgetBlacklist, 3, 0, 1, 2) self.gridLayout_6.addWidget(self.tableWidgetBlacklist, 3, 0, 1, 2)
icon7 = QtGui.QIcon() icon9 = QtGui.QIcon()
icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/blacklist.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) icon9.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/blacklist.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.blackwhitelist, icon7, _fromUtf8("")) self.tabWidget.addTab(self.blackwhitelist, icon9, _fromUtf8(""))
self.networkstatus = QtGui.QWidget() self.networkstatus = QtGui.QWidget()
self.networkstatus.setObjectName(_fromUtf8("networkstatus")) self.networkstatus.setObjectName(_fromUtf8("networkstatus"))
self.pushButtonStatusIcon = QtGui.QPushButton(self.networkstatus) self.pushButtonStatusIcon = QtGui.QPushButton(self.networkstatus)
self.pushButtonStatusIcon.setGeometry(QtCore.QRect(680, 440, 21, 23)) self.pushButtonStatusIcon.setGeometry(QtCore.QRect(680, 440, 21, 23))
self.pushButtonStatusIcon.setText(_fromUtf8("")) self.pushButtonStatusIcon.setText(_fromUtf8(""))
icon8 = QtGui.QIcon() icon10 = QtGui.QIcon()
icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/redicon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) icon10.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/redicon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButtonStatusIcon.setIcon(icon8) self.pushButtonStatusIcon.setIcon(icon10)
self.pushButtonStatusIcon.setFlat(True) self.pushButtonStatusIcon.setFlat(True)
self.pushButtonStatusIcon.setObjectName(_fromUtf8("pushButtonStatusIcon")) self.pushButtonStatusIcon.setObjectName(_fromUtf8("pushButtonStatusIcon"))
self.tableWidgetConnectionCount = QtGui.QTableWidget(self.networkstatus) self.tableWidgetConnectionCount = settingsmixin.STableWidget(self.networkstatus)
self.tableWidgetConnectionCount.setGeometry(QtCore.QRect(20, 70, 241, 241)) self.tableWidgetConnectionCount.setGeometry(QtCore.QRect(20, 70, 241, 241))
palette = QtGui.QPalette() palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(212, 208, 200)) brush = QtGui.QBrush(QtGui.QColor(212, 208, 200))
@ -470,22 +639,25 @@ class Ui_MainWindow(object):
self.labelBroadcastCount = QtGui.QLabel(self.networkstatus) self.labelBroadcastCount = QtGui.QLabel(self.networkstatus)
self.labelBroadcastCount.setGeometry(QtCore.QRect(350, 150, 351, 16)) self.labelBroadcastCount.setGeometry(QtCore.QRect(350, 150, 351, 16))
self.labelBroadcastCount.setObjectName(_fromUtf8("labelBroadcastCount")) self.labelBroadcastCount.setObjectName(_fromUtf8("labelBroadcastCount"))
self.labelSyncStatus = QtGui.QLabel(self.networkstatus)
self.labelSyncStatus.setGeometry(QtCore.QRect(350, 190, 331, 20))
self.labelSyncStatus.setObjectName(_fromUtf8("labelSyncStatus"))
self.labelLookupsPerSecond = QtGui.QLabel(self.networkstatus) self.labelLookupsPerSecond = QtGui.QLabel(self.networkstatus)
self.labelLookupsPerSecond.setGeometry(QtCore.QRect(320, 250, 291, 16)) self.labelLookupsPerSecond.setGeometry(QtCore.QRect(320, 270, 291, 16))
self.labelLookupsPerSecond.setObjectName(_fromUtf8("labelLookupsPerSecond")) self.labelLookupsPerSecond.setObjectName(_fromUtf8("labelLookupsPerSecond"))
self.labelBytesRecvCount = QtGui.QLabel(self.networkstatus) self.labelBytesRecvCount = QtGui.QLabel(self.networkstatus)
self.labelBytesRecvCount.setGeometry(QtCore.QRect(350, 210, 251, 16)) self.labelBytesRecvCount.setGeometry(QtCore.QRect(350, 230, 251, 16))
self.labelBytesRecvCount.setObjectName(_fromUtf8("labelBytesRecvCount")) self.labelBytesRecvCount.setObjectName(_fromUtf8("labelBytesRecvCount"))
self.labelBytesSentCount = QtGui.QLabel(self.networkstatus) self.labelBytesSentCount = QtGui.QLabel(self.networkstatus)
self.labelBytesSentCount.setGeometry(QtCore.QRect(350, 230, 251, 16)) self.labelBytesSentCount.setGeometry(QtCore.QRect(350, 250, 251, 16))
self.labelBytesSentCount.setObjectName(_fromUtf8("labelBytesSentCount")) self.labelBytesSentCount.setObjectName(_fromUtf8("labelBytesSentCount"))
icon9 = QtGui.QIcon() icon11 = QtGui.QIcon()
icon9.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) icon11.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.networkstatus, icon9, _fromUtf8("")) self.tabWidget.addTab(self.networkstatus, icon11, _fromUtf8(""))
self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) self.gridLayout_10.addWidget(self.tabWidget, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow) self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 885, 21)) self.menubar.setGeometry(QtCore.QRect(0, 0, 885, 27))
self.menubar.setObjectName(_fromUtf8("menubar")) self.menubar.setObjectName(_fromUtf8("menubar"))
self.menuFile = QtGui.QMenu(self.menubar) self.menuFile = QtGui.QMenu(self.menubar)
self.menuFile.setObjectName(_fromUtf8("menuFile")) self.menuFile.setObjectName(_fromUtf8("menuFile"))
@ -537,7 +709,6 @@ class Ui_MainWindow(object):
self.menuFile.addAction(self.actionManageKeys) self.menuFile.addAction(self.actionManageKeys)
self.menuFile.addAction(self.actionDeleteAllTrashedMessages) self.menuFile.addAction(self.actionDeleteAllTrashedMessages)
self.menuFile.addAction(self.actionRegenerateDeterministicAddresses) self.menuFile.addAction(self.actionRegenerateDeterministicAddresses)
self.menuFile.addAction(self.actionJoinChan)
self.menuFile.addAction(self.actionExit) self.menuFile.addAction(self.actionExit)
self.menuSettings.addAction(self.actionSettings) self.menuSettings.addAction(self.actionSettings)
self.menuHelp.addAction(self.actionHelp) self.menuHelp.addAction(self.actionHelp)
@ -548,29 +719,15 @@ class Ui_MainWindow(object):
self.retranslateUi(MainWindow) self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0)
QtCore.QObject.connect(self.radioButtonSpecific, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.lineEditTo.setEnabled) self.tabWidgetSend.setCurrentIndex(0)
QtCore.QObject.connect(self.radioButtonSpecific, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.labelSendBroadcastWarning.hide)
QtCore.QObject.connect(self.radioButtonBroadcast, QtCore.SIGNAL(_fromUtf8("clicked()")), self.labelSendBroadcastWarning.show)
QtCore.QMetaObject.connectSlotsByName(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow)
MainWindow.setTabOrder(self.tabWidget, self.tableWidgetInbox)
MainWindow.setTabOrder(self.tableWidgetInbox, self.textEditInboxMessage) MainWindow.setTabOrder(self.tableWidgetInbox, self.textEditInboxMessage)
MainWindow.setTabOrder(self.textEditInboxMessage, self.radioButtonSpecific) MainWindow.setTabOrder(self.textEditInboxMessage, self.comboBoxSendFrom)
MainWindow.setTabOrder(self.radioButtonSpecific, self.radioButtonBroadcast)
MainWindow.setTabOrder(self.radioButtonBroadcast, self.comboBoxSendFrom)
MainWindow.setTabOrder(self.comboBoxSendFrom, self.lineEditTo) MainWindow.setTabOrder(self.comboBoxSendFrom, self.lineEditTo)
MainWindow.setTabOrder(self.lineEditTo, self.pushButtonLoadFromAddressBook) MainWindow.setTabOrder(self.lineEditTo, self.lineEditSubject)
MainWindow.setTabOrder(self.pushButtonLoadFromAddressBook, self.lineEditSubject)
MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage) MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage)
MainWindow.setTabOrder(self.textEditMessage, self.pushButtonSend) MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription)
MainWindow.setTabOrder(self.pushButtonSend, self.tableWidgetSent) MainWindow.setTabOrder(self.pushButtonAddSubscription, self.radioButtonBlacklist)
MainWindow.setTabOrder(self.tableWidgetSent, self.textEditSentMessage)
MainWindow.setTabOrder(self.textEditSentMessage, self.pushButtonNewAddress)
MainWindow.setTabOrder(self.pushButtonNewAddress, self.tableWidgetYourIdentities)
MainWindow.setTabOrder(self.tableWidgetYourIdentities, self.pushButtonAddSubscription)
MainWindow.setTabOrder(self.pushButtonAddSubscription, self.tableWidgetSubscriptions)
MainWindow.setTabOrder(self.tableWidgetSubscriptions, self.pushButtonAddAddressBook)
MainWindow.setTabOrder(self.pushButtonAddAddressBook, self.tableWidgetAddressBook)
MainWindow.setTabOrder(self.tableWidgetAddressBook, self.radioButtonBlacklist)
MainWindow.setTabOrder(self.radioButtonBlacklist, self.radioButtonWhitelist) MainWindow.setTabOrder(self.radioButtonBlacklist, self.radioButtonWhitelist)
MainWindow.setTabOrder(self.radioButtonWhitelist, self.pushButtonAddBlacklist) MainWindow.setTabOrder(self.radioButtonWhitelist, self.pushButtonAddBlacklist)
MainWindow.setTabOrder(self.pushButtonAddBlacklist, self.tableWidgetBlacklist) MainWindow.setTabOrder(self.pushButtonAddBlacklist, self.tableWidgetBlacklist)
@ -579,12 +736,14 @@ class Ui_MainWindow(object):
def retranslateUi(self, MainWindow): def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None)) MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None))
self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None))
self.pushButtonNewAddress.setText(_translate("MainWindow", "New Identity", None))
self.inboxSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", None)) self.inboxSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", None))
self.inboxSearchOptionCB.setItemText(0, _translate("MainWindow", "All", None)) self.inboxSearchOption.setItemText(0, _translate("MainWindow", "All", None))
self.inboxSearchOptionCB.setItemText(1, _translate("MainWindow", "To", None)) self.inboxSearchOption.setItemText(1, _translate("MainWindow", "To", None))
self.inboxSearchOptionCB.setItemText(2, _translate("MainWindow", "From", None)) self.inboxSearchOption.setItemText(2, _translate("MainWindow", "From", None))
self.inboxSearchOptionCB.setItemText(3, _translate("MainWindow", "Subject", None)) self.inboxSearchOption.setItemText(3, _translate("MainWindow", "Subject", None))
self.inboxSearchOptionCB.setItemText(4, _translate("MainWindow", "Message", None)) self.inboxSearchOption.setItemText(4, _translate("MainWindow", "Message", None))
self.tableWidgetInbox.setSortingEnabled(True) self.tableWidgetInbox.setSortingEnabled(True)
item = self.tableWidgetInbox.horizontalHeaderItem(0) item = self.tableWidgetInbox.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "To", None)) item.setText(_translate("MainWindow", "To", None))
@ -594,66 +753,71 @@ class Ui_MainWindow(object):
item.setText(_translate("MainWindow", "Subject", None)) item.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetInbox.horizontalHeaderItem(3) item = self.tableWidgetInbox.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Received", None)) item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Inbox", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Messages", None))
self.pushButtonLoadFromAddressBook.setText(_translate("MainWindow", "Load from Address book", None)) self.tableWidgetAddressBook.setSortingEnabled(True)
item = self.tableWidgetAddressBook.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "Address book", None))
item = self.tableWidgetAddressBook.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "Address", None))
self.pushButtonAddAddressBook.setText(_translate("MainWindow", "Add Contact", None))
self.pushButtonFetchNamecoinID.setText(_translate("MainWindow", "Fetch Namecoin ID", None))
self.label_3.setText(_translate("MainWindow", "Subject:", None)) self.label_3.setText(_translate("MainWindow", "Subject:", None))
self.pushButtonSend.setText(_translate("MainWindow", "Send", None))
self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "X days", None))
self.label_4.setText(_translate("MainWindow", "Message:", None))
self.label.setText(_translate("MainWindow", "To:", None))
self.radioButtonSpecific.setText(_translate("MainWindow", "Send to one or more specific people", None))
self.labelSendBroadcastWarning.setText(_translate("MainWindow", "Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them.", None))
self.radioButtonBroadcast.setText(_translate("MainWindow", "Broadcast to everyone who is subscribed to your address", None))
self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None))
self.label_2.setText(_translate("MainWindow", "From:", None)) self.label_2.setText(_translate("MainWindow", "From:", None))
self.label.setText(_translate("MainWindow", "To:", None))
self.textEditMessage.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" 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" "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\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" "</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;\"><br /></p></body></html>", None)) "<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.pushButtonFetchNamecoinID.setText(_translate("MainWindow", "Fetch Namecoin ID", None)) self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendDirect), _translate("MainWindow", "Send ordinary Message", None))
self.label_8.setText(_translate("MainWindow", "From:", None))
self.label_7.setText(_translate("MainWindow", "Subject:", None))
self.textEditMessageBroadcast.setHtml(_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:\'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.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendBroadcast), _translate("MainWindow", "Send Message to your Subscribers", None))
self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None))
self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "X days", None))
self.pushButtonSend.setText(_translate("MainWindow", "Send", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None))
self.sentSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", None)) self.treeWidgetSubscriptions.headerItem().setText(0, _translate("MainWindow", "Subscriptions", None))
self.sentSearchOptionCB.setItemText(0, _translate("MainWindow", "All", None))
self.sentSearchOptionCB.setItemText(1, _translate("MainWindow", "To", None))
self.sentSearchOptionCB.setItemText(2, _translate("MainWindow", "From", None))
self.sentSearchOptionCB.setItemText(3, _translate("MainWindow", "Subject", None))
self.sentSearchOptionCB.setItemText(4, _translate("MainWindow", "Message", None))
self.tableWidgetSent.setSortingEnabled(True)
item = self.tableWidgetSent.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "To", None))
item = self.tableWidgetSent.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "From", None))
item = self.tableWidgetSent.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetSent.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Status", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.sent), _translate("MainWindow", "Sent", None))
self.pushButtonNewAddress.setText(_translate("MainWindow", "New", None))
self.tableWidgetYourIdentities.setSortingEnabled(True)
item = self.tableWidgetYourIdentities.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "Label (not shown to anyone)", None))
item = self.tableWidgetYourIdentities.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "Address", None))
item = self.tableWidgetYourIdentities.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "Stream", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.youridentities), _translate("MainWindow", "Your Identities", None))
self.label_5.setText(_translate("MainWindow", "Here you can subscribe to \'broadcast messages\' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab.", None))
self.pushButtonAddSubscription.setText(_translate("MainWindow", "Add new Subscription", None)) self.pushButtonAddSubscription.setText(_translate("MainWindow", "Add new Subscription", None))
self.tableWidgetSubscriptions.setSortingEnabled(True) self.inboxSearchLineEditSubscriptions.setPlaceholderText(_translate("MainWindow", "Search", None))
item = self.tableWidgetSubscriptions.horizontalHeaderItem(0) self.inboxSearchOptionSubscriptions.setItemText(0, _translate("MainWindow", "All", None))
item.setText(_translate("MainWindow", "Label", None)) self.inboxSearchOptionSubscriptions.setItemText(1, _translate("MainWindow", "To", None))
item = self.tableWidgetSubscriptions.horizontalHeaderItem(1) self.inboxSearchOptionSubscriptions.setItemText(2, _translate("MainWindow", "From", None))
item.setText(_translate("MainWindow", "Address", None)) self.inboxSearchOptionSubscriptions.setItemText(3, _translate("MainWindow", "Subject", None))
self.inboxSearchOptionSubscriptions.setItemText(4, _translate("MainWindow", "Message", None))
self.tableWidgetInboxSubscriptions.setSortingEnabled(True)
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "To", None))
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "From", None))
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None))
self.label_6.setText(_translate("MainWindow", "The Address book is useful for adding names or labels to other people\'s Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the \'Add\' button, or from your inbox by right-clicking on a message.", None)) self.treeWidgetChans.headerItem().setText(0, _translate("MainWindow", "Chans", None))
self.pushButtonAddAddressBook.setText(_translate("MainWindow", "Add new entry", None)) self.pushButtonAddChan.setText(_translate("MainWindow", "Add Chan", None))
self.tableWidgetAddressBook.setSortingEnabled(True) self.inboxSearchLineEditChans.setPlaceholderText(_translate("MainWindow", "Search", None))
item = self.tableWidgetAddressBook.horizontalHeaderItem(0) self.inboxSearchOptionChans.setItemText(0, _translate("MainWindow", "All", None))
item.setText(_translate("MainWindow", "Name or Label", None)) self.inboxSearchOptionChans.setItemText(1, _translate("MainWindow", "To", None))
item = self.tableWidgetAddressBook.horizontalHeaderItem(1) self.inboxSearchOptionChans.setItemText(2, _translate("MainWindow", "From", None))
item.setText(_translate("MainWindow", "Address", None)) self.inboxSearchOptionChans.setItemText(3, _translate("MainWindow", "Subject", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.addressbook), _translate("MainWindow", "Address Book", None)) self.inboxSearchOptionChans.setItemText(4, _translate("MainWindow", "Message", None))
self.tableWidgetInboxChans.setSortingEnabled(True)
item = self.tableWidgetInboxChans.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "To", None))
item = self.tableWidgetInboxChans.horizontalHeaderItem(1)
item.setText(_translate("MainWindow", "From", None))
item = self.tableWidgetInboxChans.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetInboxChans.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.chans), _translate("MainWindow", "Chans", None))
self.radioButtonBlacklist.setText(_translate("MainWindow", "Use a Blacklist (Allow all incoming messages except those on the Blacklist)", None)) self.radioButtonBlacklist.setText(_translate("MainWindow", "Use a Blacklist (Allow all incoming messages except those on the Blacklist)", None))
self.radioButtonWhitelist.setText(_translate("MainWindow", "Use a Whitelist (Block all incoming messages except those on the Whitelist)", None)) self.radioButtonWhitelist.setText(_translate("MainWindow", "Use a Whitelist (Block all incoming messages except those on the Whitelist)", None))
self.pushButtonAddBlacklist.setText(_translate("MainWindow", "Add new entry", None)) self.pushButtonAddBlacklist.setText(_translate("MainWindow", "Add new entry", None))
@ -669,6 +833,7 @@ class Ui_MainWindow(object):
item.setText(_translate("MainWindow", "Connections", None)) item.setText(_translate("MainWindow", "Connections", None))
self.labelTotalConnections.setText(_translate("MainWindow", "Total connections:", None)) self.labelTotalConnections.setText(_translate("MainWindow", "Total connections:", None))
self.labelStartupTime.setText(_translate("MainWindow", "Since startup:", None)) self.labelStartupTime.setText(_translate("MainWindow", "Since startup:", None))
self.labelSyncStatus.setText(_translate("MainWindow", "Objects to be synced:", None))
self.labelMessageCount.setText(_translate("MainWindow", "Processed 0 person-to-person messages.", None)) self.labelMessageCount.setText(_translate("MainWindow", "Processed 0 person-to-person messages.", None))
self.labelPubkeyCount.setText(_translate("MainWindow", "Processed 0 public keys.", None)) self.labelPubkeyCount.setText(_translate("MainWindow", "Processed 0 public keys.", None))
self.labelBroadcastCount.setText(_translate("MainWindow", "Processed 0 broadcasts.", None)) self.labelBroadcastCount.setText(_translate("MainWindow", "Processed 0 broadcasts.", None))
@ -692,3 +857,14 @@ class Ui_MainWindow(object):
self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None)) self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None))
import bitmessage_icons_rc import bitmessage_icons_rc
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = settingsmixin.SMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

View File

@ -21,10 +21,7 @@
<enum>QTabWidget::Rounded</enum> <enum>QTabWidget::Rounded</enum>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout_10">
<property name="margin">
<number>0</number>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy"> <property name="sizePolicy">
@ -65,134 +62,176 @@
<normaloff>:/newPrefix/images/inbox.png</normaloff>:/newPrefix/images/inbox.png</iconset> <normaloff>:/newPrefix/images/inbox.png</normaloff>:/newPrefix/images/inbox.png</iconset>
</attribute> </attribute>
<attribute name="title"> <attribute name="title">
<string>Inbox</string> <string>Messages</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QGridLayout" name="gridLayout">
<item> <item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayoutSearch"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item> <item>
<widget class="QLineEdit" name="inboxSearchLineEdit"> <layout class="QVBoxLayout" name="verticalLayout_12">
<property name="placeholderText"> <item>
<string>Search</string> <widget class="QTreeWidget" name="treeWidgetYourIdentities">
</property> <property name="maximumSize">
</widget> <size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<column>
<property name="text">
<string>Identities</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/identities.png</selectedoff>
</iconset>
</property>
</column>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonNewAddress">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>New Indentitiy</string>
</property>
</widget>
</item>
</layout>
</item> </item>
<item> <item>
<widget class="QComboBox" name="inboxSearchOptionCB"> <layout class="QVBoxLayout" name="verticalLayout_7">
<item> <item>
<property name="text"> <layout class="QHBoxLayout" name="horizontalLayoutSearch">
<string>All</string> <property name="topMargin">
</property> <number>0</number>
</property>
<item>
<widget class="QLineEdit" name="inboxSearchLineEdit">
<property name="placeholderText">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="inboxSearchOption">
<item>
<property name="text">
<string>All</string>
</property>
</item>
<item>
<property name="text">
<string>To</string>
</property>
</item>
<item>
<property name="text">
<string>From</string>
</property>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
</item>
</widget>
</item>
</layout>
</item> </item>
<item> <item>
<property name="text"> <widget class="QTableWidget" name="tableWidgetInbox">
<string>To</string> <property name="editTriggers">
</property> <set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
</item> </item>
<item> <item>
<property name="text"> <widget class="QTextEdit" name="textEditInboxMessage">
<string>From</string> <property name="baseSize">
</property> <size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item> </item>
<item> </layout>
<property name="text">
<string>Subject</string>
</property>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
</item>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QTableWidget" name="tableWidgetInbox">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
<widget class="QTextEdit" name="textEditInboxMessage">
<property name="baseSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="send"> <widget class="QWidget" name="send">
@ -203,498 +242,366 @@
<attribute name="title"> <attribute name="title">
<string>Send</string> <string>Send</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_7">
<item row="3" column="2"> <item row="0" column="0">
<widget class="QPushButton" name="pushButtonLoadFromAddressBook">
<property name="font">
<font>
<pointsize>7</pointsize>
</font>
</property>
<property name="text">
<string>Load from Address book</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Subject:</string>
</property>
</widget>
</item>
<item row="7" column="8">
<widget class="QPushButton" name="pushButtonSend">
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
<item row="7" column="6">
<widget class="QSlider" name="horizontalSliderTTL">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
</widget>
</item>
<item row="6" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>297</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBoxSendFrom">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="7" column="7">
<widget class="QLabel" name="labelHumanFriendlyTTLDescription">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>X days</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Message:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>To:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="radioButtonSpecific">
<property name="text">
<string>Send to one or more specific people</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="1" colspan="4">
<widget class="QLabel" name="labelSendBroadcastWarning">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them.</string>
</property>
<property name="indent">
<number>-1</number>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QRadioButton" name="radioButtonBroadcast">
<property name="text">
<string>Broadcast to everyone who is subscribed to your address</string>
</property>
</widget>
</item>
<item row="7" column="5">
<widget class="QPushButton" name="pushButtonTTL">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>TTL:</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>From:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="lineEditTo"/>
</item>
<item row="5" column="1" rowspan="2" colspan="8">
<widget class="QTextEdit" name="textEditMessage">
<property name="html">
<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>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="pushButtonFetchNamecoinID">
<property name="font">
<font>
<pointsize>7</pointsize>
</font>
</property>
<property name="text">
<string>Fetch Namecoin ID</string>
</property>
</widget>
</item>
<item row="2" column="2" colspan="7">
<widget class="QLabel" name="labelFrom">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1" colspan="8">
<widget class="QLineEdit" name="lineEditSubject">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="sent">
<attribute name="icon">
<iconset resource="bitmessage_icons.qrc">
<normaloff>:/newPrefix/images/sent.png</normaloff>:/newPrefix/images/sent.png</iconset>
</attribute>
<attribute name="title">
<string>Sent</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>0</number>
</property>
<item> <item>
<widget class="QLineEdit" name="sentSearchLineEdit"> <layout class="QVBoxLayout" name="verticalLayout_2">
<property name="placeholderText"> <item>
<string>Search</string> <widget class="QTableWidget" name="tableWidgetAddressBook">
</property> <property name="maximumSize">
</widget> <size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Address book</string>
</property>
<property name="icon">
<iconset>
<selectedoff>:/newPrefix/images/addressbook.png</selectedoff>
</iconset>
</property>
</column>
<column>
<property name="text">
<string>Address</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonAddAddressBook">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Add Contact</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonFetchNamecoinID">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Fetch Namecoin ID</string>
</property>
</widget>
</item>
</layout>
</item> </item>
<item> <item>
<widget class="QComboBox" name="sentSearchOptionCB"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<property name="text"> <widget class="QTabWidget" name="tabWidgetSend">
<string>All</string> <property name="currentIndex">
</property> <number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Send ordinary Message</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Subject:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>From:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEditSubject">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>To:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxSendFrom">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditTo"/>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEditMessage">
<property name="html">
<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:'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>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Send Message to your Subscribers</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>From:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditSubjectBroadcast">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Subject:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBoxSendFromBroadcast">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEditMessageBroadcast">
<property name="html">
<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:'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>
</layout>
</item>
</layout>
</widget>
</widget>
</item> </item>
<item> <item>
<property name="text"> <layout class="QHBoxLayout" name="horizontalLayout_5">
<string>To</string> <item>
</property> <widget class="QPushButton" name="pushButtonTTL">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>TTL:</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="horizontalSliderTTL">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelHumanFriendlyTTLDescription">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>X days</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonSend">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
</layout>
</item> </item>
<item> </layout>
<property name="text">
<string>From</string>
</property>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
</item>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QTableWidget" name="tableWidgetSent">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>130</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Status</string>
</property>
</column>
</widget>
<widget class="QTextEdit" name="textEditSentMessage">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="youridentities">
<attribute name="icon">
<iconset resource="bitmessage_icons.qrc">
<normaloff>:/newPrefix/images/identities.png</normaloff>:/newPrefix/images/identities.png</iconset>
</attribute>
<attribute name="title">
<string>Your Identities</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QPushButton" name="pushButtonNewAddress">
<property name="text">
<string>New</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>689</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTableWidget" name="tableWidgetYourIdentities">
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>346</number>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>52</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<attribute name="verticalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Label (not shown to anyone)</string>
</property>
<property name="font">
<font>
<kerning>true</kerning>
</font>
</property>
</column>
<column>
<property name="text">
<string>Address</string>
</property>
</column>
<column>
<property name="text">
<string>Stream</string>
</property>
</column>
</widget>
</item>
</layout> </layout>
<zorder></zorder>
</widget> </widget>
<widget class="QWidget" name="subscriptions"> <widget class="QWidget" name="subscriptions">
<attribute name="icon"> <attribute name="icon">
@ -704,162 +611,369 @@ p, li { white-space: pre-wrap; }
<attribute name="title"> <attribute name="title">
<string>Subscriptions</string> <string>Subscriptions</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" colspan="2"> <item row="0" column="0">
<widget class="QLabel" name="label_5"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="text"> <item>
<string>Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab.</string> <layout class="QVBoxLayout" name="verticalLayout_3">
</property> <item>
<property name="wordWrap"> <widget class="QTreeWidget" name="treeWidgetSubscriptions">
<bool>true</bool> <property name="maximumSize">
</property> <size>
</widget> <width>200</width>
</item> <height>16777215</height>
<item row="1" column="0"> </size>
<widget class="QPushButton" name="pushButtonAddSubscription"> </property>
<property name="text"> <property name="alternatingRowColors">
<string>Add new Subscription</string> <bool>true</bool>
</property> </property>
</widget> <property name="selectionMode">
</item> <enum>QAbstractItemView::SingleSelection</enum>
<item row="1" column="1"> </property>
<spacer name="horizontalSpacer_2"> <property name="selectionBehavior">
<property name="orientation"> <enum>QAbstractItemView::SelectRows</enum>
<enum>Qt::Horizontal</enum> </property>
</property> <column>
<property name="sizeHint" stdset="0"> <property name="text">
<size> <string>Subscriptions</string>
<width>689</width> </property>
<height>20</height> <property name="icon">
</size> <iconset>
</property> <selectedoff>:/newPrefix/images/subscriptions.png</selectedoff>
</spacer> </iconset>
</item> </property>
<item row="2" column="0" colspan="2"> </column>
<widget class="QTableWidget" name="tableWidgetSubscriptions"> </widget>
<property name="alternatingRowColors"> </item>
<bool>true</bool> <item>
</property> <widget class="QPushButton" name="pushButtonAddSubscription">
<property name="selectionMode"> <property name="maximumSize">
<enum>QAbstractItemView::SingleSelection</enum> <size>
</property> <width>200</width>
<property name="selectionBehavior"> <height>16777215</height>
<enum>QAbstractItemView::SelectRows</enum> </size>
</property> </property>
<property name="sortingEnabled"> <property name="text">
<bool>true</bool> <string>Add new Subscription</string>
</property> </property>
<attribute name="horizontalHeaderCascadingSectionResizes"> </widget>
<bool>true</bool> </item>
</attribute> </layout>
<attribute name="horizontalHeaderDefaultSectionSize"> </item>
<number>400</number> <item>
</attribute> <layout class="QVBoxLayout" name="verticalLayout_4">
<attribute name="horizontalHeaderHighlightSections"> <item>
<bool>false</bool> <layout class="QHBoxLayout" name="horizontalLayout_2">
</attribute> <item>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0"> <widget class="QLineEdit" name="inboxSearchLineEditSubscriptions">
<bool>false</bool> <property name="placeholderText">
</attribute> <string>Search</string>
<attribute name="horizontalHeaderStretchLastSection"> </property>
<bool>true</bool> </widget>
</attribute> </item>
<attribute name="verticalHeaderVisible"> <item>
<bool>false</bool> <widget class="QComboBox" name="inboxSearchOptionSubscriptions">
</attribute> <item>
<column> <property name="text">
<property name="text"> <string>All</string>
<string>Label</string> </property>
</property> </item>
</column> <item>
<column> <property name="text">
<property name="text"> <string>To</string>
<string>Address</string> </property>
</property> </item>
</column> <item>
</widget> <property name="text">
<string>From</string>
</property>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableWidgetInboxSubscriptions">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QTextEdit" name="textEditInboxMessageSubscriptions">
<property name="baseSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="addressbook"> <widget class="QWidget" name="tab_3">
<attribute name="icon"> <attribute name="icon">
<iconset resource="bitmessage_icons.qrc"> <iconset resource="bitmessage_icons.qrc">
<normaloff>:/newPrefix/images/addressbook.png</normaloff>:/newPrefix/images/addressbook.png</iconset> <normaloff>:/newPrefix/images/can-icon-16px.png</normaloff>:/newPrefix/images/can-icon-16px.png</iconset>
</attribute> </attribute>
<attribute name="title"> <attribute name="title">
<string>Address Book</string> <string>Chans</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_5"> <layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0" colspan="2"> <item row="0" column="0">
<widget class="QLabel" name="label_6"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="text"> <item>
<string>The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message.</string> <layout class="QVBoxLayout" name="verticalLayout_17">
</property> <item>
<property name="wordWrap"> <widget class="QTreeWidget" name="treeWidgetChans">
<bool>true</bool> <property name="maximumSize">
</property> <size>
</widget> <width>200</width>
</item> <height>16777215</height>
<item row="1" column="0"> </size>
<widget class="QPushButton" name="pushButtonAddAddressBook"> </property>
<property name="text"> <property name="frameShadow">
<string>Add new entry</string> <enum>QFrame::Sunken</enum>
</property> </property>
</widget> <property name="lineWidth">
</item> <number>1</number>
<item row="1" column="1"> </property>
<spacer name="horizontalSpacer_5"> <property name="alternatingRowColors">
<property name="orientation"> <bool>true</bool>
<enum>Qt::Horizontal</enum> </property>
</property> <property name="selectionMode">
<property name="sizeHint" stdset="0"> <enum>QAbstractItemView::SingleSelection</enum>
<size> </property>
<width>689</width> <property name="selectionBehavior">
<height>20</height> <enum>QAbstractItemView::SelectRows</enum>
</size> </property>
</property> <column>
</spacer> <property name="text">
</item> <string>Chans</string>
<item row="2" column="0" colspan="2"> </property>
<widget class="QTableWidget" name="tableWidgetAddressBook"> <property name="icon">
<property name="alternatingRowColors"> <iconset>
<bool>true</bool> <selectedoff>:/newPrefix/images/can-icon-16px.png</selectedoff>
</property> </iconset>
<property name="selectionMode"> </property>
<enum>QAbstractItemView::ExtendedSelection</enum> </column>
</property> </widget>
<property name="selectionBehavior"> </item>
<enum>QAbstractItemView::SelectRows</enum> <item>
</property> <widget class="QPushButton" name="pushButtonAddChan">
<property name="sortingEnabled"> <property name="maximumSize">
<bool>true</bool> <size>
</property> <width>200</width>
<attribute name="horizontalHeaderCascadingSectionResizes"> <height>16777215</height>
<bool>true</bool> </size>
</attribute> </property>
<attribute name="horizontalHeaderDefaultSectionSize"> <property name="text">
<number>400</number> <string>Add Chan</string>
</attribute> </property>
<attribute name="horizontalHeaderHighlightSections"> </widget>
<bool>false</bool> </item>
</attribute> </layout>
<attribute name="horizontalHeaderStretchLastSection"> </item>
<bool>true</bool> <item>
</attribute> <layout class="QVBoxLayout" name="verticalLayout_8">
<attribute name="verticalHeaderVisible"> <item>
<bool>false</bool> <layout class="QHBoxLayout" name="horizontalLayout_6">
</attribute> <item>
<column> <widget class="QLineEdit" name="inboxSearchLineEditChans">
<property name="text"> <property name="placeholderText">
<string>Name or Label</string> <string>Search</string>
</property> </property>
</column> </widget>
<column> </item>
<property name="text"> <item>
<string>Address</string> <widget class="QComboBox" name="inboxSearchOptionChans">
</property> <item>
</column> <property name="text">
</widget> <string>All</string>
</property>
</item>
<item>
<property name="text">
<string>To</string>
</property>
</item>
<item>
<property name="text">
<string>From</string>
</property>
</item>
<item>
<property name="text">
<string>Subject</string>
</property>
</item>
<item>
<property name="text">
<string>Message</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableWidgetInboxChans">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>200</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>27</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>26</number>
</attribute>
<column>
<property name="text">
<string>To</string>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Subject</string>
</property>
</column>
<column>
<property name="text">
<string>Received</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QTextEdit" name="textEditInboxMessageChans">
<property name="baseSize">
<size>
<width>0</width>
<height>500</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -1182,7 +1296,7 @@ p, li { white-space: pre-wrap; }
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>885</width> <width>885</width>
<height>21</height> <height>27</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
@ -1192,7 +1306,6 @@ p, li { white-space: pre-wrap; }
<addaction name="actionManageKeys"/> <addaction name="actionManageKeys"/>
<addaction name="actionDeleteAllTrashedMessages"/> <addaction name="actionDeleteAllTrashedMessages"/>
<addaction name="actionRegenerateDeterministicAddresses"/> <addaction name="actionRegenerateDeterministicAddresses"/>
<addaction name="actionJoinChan"/>
<addaction name="actionExit"/> <addaction name="actionExit"/>
</widget> </widget>
<widget class="QMenu" name="menuSettings"> <widget class="QMenu" name="menuSettings">
@ -1319,25 +1432,13 @@ p, li { white-space: pre-wrap; }
</action> </action>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>tableWidgetInbox</tabstop> <tabstop>tableWidgetInbox</tabstop>
<tabstop>textEditInboxMessage</tabstop> <tabstop>textEditInboxMessage</tabstop>
<tabstop>radioButtonSpecific</tabstop>
<tabstop>radioButtonBroadcast</tabstop>
<tabstop>comboBoxSendFrom</tabstop> <tabstop>comboBoxSendFrom</tabstop>
<tabstop>lineEditTo</tabstop> <tabstop>lineEditTo</tabstop>
<tabstop>pushButtonLoadFromAddressBook</tabstop>
<tabstop>lineEditSubject</tabstop> <tabstop>lineEditSubject</tabstop>
<tabstop>textEditMessage</tabstop> <tabstop>textEditMessage</tabstop>
<tabstop>pushButtonSend</tabstop>
<tabstop>tableWidgetSent</tabstop>
<tabstop>textEditSentMessage</tabstop>
<tabstop>pushButtonNewAddress</tabstop>
<tabstop>tableWidgetYourIdentities</tabstop>
<tabstop>pushButtonAddSubscription</tabstop> <tabstop>pushButtonAddSubscription</tabstop>
<tabstop>tableWidgetSubscriptions</tabstop>
<tabstop>pushButtonAddAddressBook</tabstop>
<tabstop>tableWidgetAddressBook</tabstop>
<tabstop>radioButtonBlacklist</tabstop> <tabstop>radioButtonBlacklist</tabstop>
<tabstop>radioButtonWhitelist</tabstop> <tabstop>radioButtonWhitelist</tabstop>
<tabstop>pushButtonAddBlacklist</tabstop> <tabstop>pushButtonAddBlacklist</tabstop>
@ -1348,54 +1449,5 @@ p, li { white-space: pre-wrap; }
<resources> <resources>
<include location="bitmessage_icons.qrc"/> <include location="bitmessage_icons.qrc"/>
</resources> </resources>
<connections> <connections/>
<connection>
<sender>radioButtonSpecific</sender>
<signal>toggled(bool)</signal>
<receiver>lineEditTo</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>121</x>
<y>60</y>
</hint>
<hint type="destinationlabel">
<x>175</x>
<y>147</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioButtonSpecific</sender>
<signal>clicked(bool)</signal>
<receiver>labelSendBroadcastWarning</receiver>
<slot>hide()</slot>
<hints>
<hint type="sourcelabel">
<x>95</x>
<y>59</y>
</hint>
<hint type="destinationlabel">
<x>129</x>
<y>528</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioButtonBroadcast</sender>
<signal>clicked()</signal>
<receiver>labelSendBroadcastWarning</receiver>
<slot>show()</slot>
<hints>
<hint type="sourcelabel">
<x>108</x>
<y>84</y>
</hint>
<hint type="destinationlabel">
<x>177</x>
<y>519</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View File

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'emailgateway.ui'
#
# Created: Fri Apr 26 17:43:31 2013
# by: PyQt4 UI code generator 4.9.4
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_EmailGatewayDialog(object):
def setupUi(self, EmailGatewayDialog):
EmailGatewayDialog.setObjectName(_fromUtf8("EmailGatewayDialog"))
EmailGatewayDialog.resize(386, 172)
self.gridLayout = QtGui.QGridLayout(EmailGatewayDialog)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.radioButtonRegister = QtGui.QRadioButton(EmailGatewayDialog)
self.radioButtonRegister.setChecked(True)
self.radioButtonRegister.setObjectName(_fromUtf8("radioButtonRegister"))
self.gridLayout.addWidget(self.radioButtonRegister, 1, 0, 1, 1)
self.radioButtonUnregister = QtGui.QRadioButton(EmailGatewayDialog)
self.radioButtonUnregister.setObjectName(_fromUtf8("radioButtonUnregister"))
self.gridLayout.addWidget(self.radioButtonUnregister, 4, 0, 1, 1)
self.label = QtGui.QLabel(EmailGatewayDialog)
self.label.setWordWrap(True)
self.label.setObjectName(_fromUtf8("label"))
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.label_2 = QtGui.QLabel(EmailGatewayDialog)
self.label_2.setObjectName(_fromUtf8("label_2"))
self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
self.lineEditEmail = QtGui.QLineEdit(EmailGatewayDialog)
self.lineEditEmail.setEnabled(True)
self.lineEditEmail.setObjectName(_fromUtf8("lineEditEmail"))
self.gridLayout.addWidget(self.lineEditEmail, 3, 0, 1, 1)
self.buttonBox = QtGui.QDialogButtonBox(EmailGatewayDialog)
self.buttonBox.setMinimumSize(QtCore.QSize(368, 0))
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.gridLayout.addWidget(self.buttonBox, 5, 0, 1, 1)
self.retranslateUi(EmailGatewayDialog)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), EmailGatewayDialog.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), EmailGatewayDialog.reject)
QtCore.QObject.connect(self.radioButtonRegister, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setEnabled)
QtCore.QObject.connect(self.radioButtonUnregister, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setDisabled)
QtCore.QMetaObject.connectSlotsByName(EmailGatewayDialog)
EmailGatewayDialog.setTabOrder(self.radioButtonRegister, self.lineEditEmail)
EmailGatewayDialog.setTabOrder(self.lineEditEmail, self.radioButtonUnregister)
EmailGatewayDialog.setTabOrder(self.radioButtonUnregister, self.buttonBox)
def retranslateUi(self, EmailGatewayDialog):
EmailGatewayDialog.setWindowTitle(QtGui.QApplication.translate("EmailGatewayDialog", "Email gateway", None, QtGui.QApplication.UnicodeUTF8))
self.radioButtonRegister.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Register on email gateway", None, QtGui.QApplication.UnicodeUTF8))
self.radioButtonUnregister.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Unregister from email gateway", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available.", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Desired email address (including @mailchuck.com):", None, QtGui.QApplication.UnicodeUTF8))

View File

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EmailGatewayDialog</class>
<widget class="QDialog" name="EmailGatewayDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>386</width>
<height>172</height>
</rect>
</property>
<property name="windowTitle">
<string>Email gateway</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Desired email address (including @mailchuck.com)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="radioButtonRegister">
<property name="text">
<string>Register on email gateway</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="minimumSize">
<size>
<width>368</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLineEdit" name="lineEditEmailAddress">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>@mailchuck.com</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Email gateway alows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QRadioButton" name="radioButtonUnregister">
<property name="text">
<string>Unregister from email gateway</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>radioButtonRegister</tabstop>
<tabstop>lineEditEmailAddress</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>EmailGatewayDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>227</x>
<y>152</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>171</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>EmailGatewayDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>295</x>
<y>158</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>171</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioButtonRegister</sender>
<signal>clicked(bool)</signal>
<receiver>lineEditEmailAddress</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>95</x>
<y>40</y>
</hint>
<hint type="destinationlabel">
<x>94</x>
<y>123</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioButtonUnregister</sender>
<signal>clicked(bool)</signal>
<receiver>lineEditEmailAddress</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>139</x>
<y>19</y>
</hint>
<hint type="destinationlabel">
<x>187</x>
<y>123</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,303 @@
from PyQt4 import QtCore, QtGui
from helper_sql import *
from utils import *
import shared
from settingsmixin import SettingsMixin
class AccountMixin (object):
ALL = 0
NORMAL = 1
CHAN = 2
MAILINGLIST = 3
SUBSCRIPTION = 4
BROADCAST = 5
def accountColor (self):
if not self.isEnabled:
return QtGui.QColor(128, 128, 128)
elif self.type == self.CHAN:
return QtGui.QColor(216, 119, 0)
elif self.type in [self.MAILINGLIST, self.SUBSCRIPTION]:
return QtGui.QColor(137, 04, 177)
else:
return QtGui.QApplication.palette().text().color()
def folderColor (self):
if not self.parent.isEnabled:
return QtGui.QColor(128, 128, 128)
else:
return QtGui.QApplication.palette().text().color()
def accountBrush(self):
brush = QtGui.QBrush(self.accountColor())
brush.setStyle(QtCore.Qt.NoBrush)
return brush
def folderBrush(self):
brush = QtGui.QBrush(self.folderColor())
brush.setStyle(QtCore.Qt.NoBrush)
return brush
def setAddress(self, address):
if address is None:
self.address = None
else:
self.address = str(address)
self.updateText()
def setUnreadCount(self, cnt):
self.unreadCount = int(cnt)
self.updateText()
def setEnabled(self, enabled):
self.isEnabled = enabled
if hasattr(self, "setExpanded"):
self.setExpanded(enabled)
if isinstance(self, Ui_AddressWidget):
for i in range(self.childCount()):
if isinstance(self.child(i), Ui_FolderWidget):
self.child(i).setEnabled(enabled)
self.updateText()
def setType(self):
if self.address is None:
self.type = self.ALL
elif shared.safeConfigGetBoolean(self.address, 'chan'):
self.type = self.CHAN
elif shared.safeConfigGetBoolean(self.address, 'mailinglist'):
self.type = self.MAILINGLIST
else:
self.type = self.NORMAL
def updateText(self):
pass
class Ui_FolderWidget(QtGui.QTreeWidgetItem, AccountMixin):
folderWeight = {"inbox": 1, "new": 2, "sent": 3, "trash": 4}
def __init__(self, parent, pos = 0, address = "", folderName = "", unreadCount = 0):
super(QtGui.QTreeWidgetItem, self).__init__()
self.initialised = False
self.setAddress(address)
self.setFolderName(folderName)
self.setUnreadCount(unreadCount)
self.parent = parent
self.initialised = True
self.updateText()
parent.insertChild(pos, self)
def setFolderName(self, fname):
self.folderName = str(fname)
self.setData(0, QtCore.Qt.UserRole, self.folderName)
self.updateText()
def updateText(self):
if not self.initialised:
return
text = QtGui.QApplication.translate("MainWindow", self.folderName)
font = QtGui.QFont()
if self.unreadCount > 0:
text += " (" + str(self.unreadCount) + ")"
font.setBold(True)
else:
font.setBold(False)
self.setFont(0, font)
self.setForeground(0, self.folderBrush())
self.setText(0, text)
self.setToolTip(0, text)
# self.setData(0, QtCore.Qt.UserRole, [self.address, self.folderName])
# inbox, sent, thrash first, rest alphabetically
def __lt__(self, other):
if (isinstance(other, Ui_FolderWidget)):
if self.folderName in self.folderWeight:
x = self.folderWeight[self.folderName]
else:
x = 99
if other.folderName in self.folderWeight:
y = self.folderWeight[other.folderName]
else:
y = 99
reverse = False
if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder:
reverse = True
if x == y:
return self.folderName < other.folderName
else:
return (x >= y if reverse else x < y)
return super(QtGui.QTreeWidgetItem, self).__lt__(other)
class Ui_AddressWidget(QtGui.QTreeWidgetItem, AccountMixin, SettingsMixin):
def __init__(self, parent, pos = 0, address = None, unreadCount = 0, enabled = True):
super(QtGui.QTreeWidgetItem, self).__init__()
parent.insertTopLevelItem(pos, self)
# only set default when creating
#super(QtGui.QTreeWidgetItem, self).setExpanded(shared.config.getboolean(self.address, 'enabled'))
self.initialised = False
self.setAddress(address)
self.setEnabled(enabled)
self.setUnreadCount(unreadCount)
self.initialised = True
self.setType() # does updateText
def _getLabel(self):
if self.address is None:
return unicode(str(QtGui.QApplication.translate("MainWindow", "All accounts")), 'utf-8')
else:
try:
return unicode(shared.config.get(self.address, 'label'), 'utf-8)')
except:
return unicode(self.address, 'utf-8')
def _getAddressBracket(self, unreadCount = False):
ret = ""
if unreadCount:
ret += " (" + str(self.unreadCount) + ")"
if self.address is not None:
ret += " (" + self.address + ")"
return ret
def data(self, column, role):
if column == 0:
if role == QtCore.Qt.DisplayRole:
if self.unreadCount > 0 and not self.isExpanded():
return self._getLabel() + self._getAddressBracket(True)
else:
return self._getLabel() + self._getAddressBracket(False)
elif role == QtCore.Qt.EditRole:
return self._getLabel()
elif role == QtCore.Qt.ToolTipRole:
return self._getLabel() + self._getAddressBracket(False)
elif role == QtCore.Qt.DecorationRole:
if self.address is None:
return avatarize(self._getLabel())
else:
return avatarize(self.address)
elif role == QtCore.Qt.FontRole:
font = QtGui.QFont()
font.setBold(self.unreadCount > 0)
return font
elif role == QtCore.Qt.ForegroundRole:
return self.accountBrush()
return super(Ui_AddressWidget, self).data(column, role)
def setData(self, column, role, value):
if role == QtCore.Qt.EditRole:
shared.config.set(str(self.address), 'label', str(value.toString()))
shared.writeKeysFile()
return
return super(Ui_AddressWidget, self).setData(column, role, value)
def setAddress(self, address):
super(Ui_AddressWidget, self).setAddress(address)
self.setData(0, QtCore.Qt.UserRole, self.address)
def updateText(self):
if not self.initialised:
return
self.emitDataChanged()
def setExpanded(self, expand):
super(Ui_AddressWidget, self).setExpanded(expand)
self.updateText()
def _getSortRank(self):
ret = self.type
if not self.isEnabled:
ret += 100
return ret
# label (or address) alphabetically, disabled at the end
def __lt__(self, other):
if (isinstance(other, Ui_AddressWidget)):
reverse = False
if self.treeWidget().header().sortIndicatorOrder() == QtCore.Qt.DescendingOrder:
reverse = True
if self._getSortRank() == other._getSortRank():
x = self._getLabel().decode('utf-8').lower()
y = other._getLabel().decode('utf-8').lower()
return x < y
return (not reverse if self._getSortRank() < other._getSortRank() else reverse)
return super(QtGui.QTreeWidgetItem, self).__lt__(other)
class Ui_SubscriptionWidget(Ui_AddressWidget, AccountMixin):
def __init__(self, parent, pos = 0, address = "", unreadCount = 0, label = "", enabled = True):
super(QtGui.QTreeWidgetItem, self).__init__()
parent.insertTopLevelItem(pos, self)
# only set default when creating
#super(QtGui.QTreeWidgetItem, self).setExpanded(shared.config.getboolean(self.address, 'enabled'))
self.initialised = False
self.setAddress(address)
self.setEnabled(enabled)
self.setType()
self.setLabel(label)
self.initialised = True
self.setUnreadCount (unreadCount) # does updateText
def setLabel(self, label):
self.label = label
def _getLabel(self):
return unicode(self.label, 'utf-8)')
def setType(self):
self.type = self.SUBSCRIPTION
def setData(self, column, role, value):
if role == QtCore.Qt.EditRole:
self.setLabel(str(value.toString()))
sqlExecute(
'''UPDATE subscriptions SET label=? WHERE address=?''',
self.label, self.address)
return
return super(Ui_SubscriptionWidget, self).setData(column, role, value)
def updateText(self):
if not self.initialised:
return
self.emitDataChanged()
class Ui_AddressBookWidgetItem(QtGui.QTableWidgetItem, AccountMixin):
def __init__ (self, text, type = AccountMixin.NORMAL):
super(QtGui.QTableWidgetItem, self).__init__(text)
self.label = text
self.type = type
self.setEnabled(True)
self.setForeground(self.accountBrush())
def __lt__ (self, other):
if (isinstance(other, Ui_AddressBookWidgetItem)):
reverse = False
if self.tableWidget().horizontalHeader().sortIndicatorOrder() == QtCore.Qt.DescendingOrder:
reverse = True
if self.type == other.type:
return self.label.decode('utf-8').lower() < other.label.decode('utf-8').lower()
else:
return (not reverse if self.type < other.type else reverse)
return super(QtGui.QTableWidgetItem, self).__lt__(other)
class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem):
def __init__ (self, address, label, type):
Ui_AddressBookWidgetItem.__init__(self, label, type)
self.address = address
self.label = label
self.setIcon(avatarize(address))
self.setToolTip(label + " (" + address + ")")
def setLabel(self, label):
self.label = label
self.setToolTip(self.label + " (" + self.address + ")")
class Ui_AddressBookWidgetItemAddress(Ui_AddressBookWidgetItem):
def __init__ (self, address, label, type):
Ui_AddressBookWidgetItem.__init__(self, address, type)
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.setToolTip(label + " (" + address + ")")

View File

@ -0,0 +1,84 @@
#!/usr/bin/env python2.7
from PyQt4 import QtCore, QtGui
class MigrationWizardIntroPage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("Migrating configuration")
label = QtGui.QLabel("This wizard will help you to migrate your configuration. "
"You can still keep using PyBitMessage once you migrate, the changes are backwards compatible.")
label.setWordWrap(True)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
def nextId(self):
return 1
class MigrationWizardAddressesPage(QtGui.QWizardPage):
def __init__(self, addresses):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("Addresses")
label = QtGui.QLabel("Please select addresses that you are already using with mailchuck. ")
label.setWordWrap(True)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
def nextId(self):
return 10
class MigrationWizardGPUPage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("GPU")
label = QtGui.QLabel("Are you using a GPU? ")
label.setWordWrap(True)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
def nextId(self):
return 10
class MigrationWizardConclusionPage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("All done!")
label = QtGui.QLabel("You successfully migrated.")
label.setWordWrap(True)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
class Ui_MigrationWizard(QtGui.QWizard):
def __init__(self, addresses):
super(QtGui.QWizard, self).__init__()
self.pages = {}
page = MigrationWizardIntroPage()
self.setPage(0, page)
self.setStartId(0)
page = MigrationWizardAddressesPage(addresses)
self.setPage(1, page)
page = MigrationWizardGPUPage()
self.setPage(2, page)
page = MigrationWizardConclusionPage()
self.setPage(10, page)
self.setWindowTitle("Migration from PyBitMessage wizard")
self.adjustSize()
self.show()

View File

@ -0,0 +1,354 @@
#!/usr/bin/env python2.7
from PyQt4 import QtCore, QtGui
class NewAddressWizardIntroPage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("Creating a new address")
label = QtGui.QLabel("This wizard will help you create as many addresses as you like. Indeed, creating and abandoning addresses is encouraged.\n\n"
"What type of address would you like? Would you like to send emails or not?\n"
"You can still change your mind later, and register/unregister with an email service provider.\n\n")
label.setWordWrap(True)
self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage address")
self.onlyBM = QtGui.QRadioButton("Bitmessage-only address (no email)")
self.emailAsWell.setChecked(True)
self.registerField("emailAsWell", self.emailAsWell)
self.registerField("onlyBM", self.onlyBM)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
layout.addWidget(self.emailAsWell)
layout.addWidget(self.onlyBM)
self.setLayout(layout)
def nextId(self):
if self.emailAsWell.isChecked():
return 4
else:
return 1
class NewAddressWizardRngPassphrasePage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("Random or Passphrase")
label = QtGui.QLabel("<html><head/><body><p>You may generate addresses by using either random numbers or by using a passphrase. "
"If you use a passphrase, the address is called a &quot;deterministic&quot; address. "
"The \'Random Number\' option is selected by default but deterministic addresses have several pros and cons:</p>"
"<table border=0><tr><td><span style=\" font-weight:600;\">Pros:</span></td><td><span style=\" font-weight:600;\">Cons:</span></td></tr>"
"<tr><td>You can recreate your addresses on any computer from memory. "
"You need-not worry about backing up your keys.dat file as long as you can remember your passphrase.</td>"
"<td>You must remember (or write down) your passphrase if you expect to be able "
"to recreate your keys if they are lost. "
# "You must remember the address version number and the stream number along with your passphrase. "
"If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you."
"</p></body></html>")
label.setWordWrap(True)
self.randomAddress = QtGui.QRadioButton("Use a random number generator to make an address")
self.deterministicAddress = QtGui.QRadioButton("Use a passphrase to make an address")
self.randomAddress.setChecked(True)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
layout.addWidget(self.randomAddress)
layout.addWidget(self.deterministicAddress)
self.setLayout(layout)
def nextId(self):
if self.randomAddress.isChecked():
return 2
else:
return 3
class NewAddressWizardRandomPage(QtGui.QWizardPage):
def __init__(self, addresses):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("Random")
label = QtGui.QLabel("Random address.")
label.setWordWrap(True)
labelLabel = QtGui.QLabel("Label (not shown to anyone except you):")
self.labelLineEdit = QtGui.QLineEdit()
self.radioButtonMostAvailable = QtGui.QRadioButton("Use the most available stream\n"
"(best if this is the first of many addresses you will create)")
self.radioButtonExisting = QtGui.QRadioButton("Use the same stream as an existing address\n"
"(saves you some bandwidth and processing power)")
self.radioButtonMostAvailable.setChecked(True)
self.comboBoxExisting = QtGui.QComboBox()
self.comboBoxExisting.setEnabled(False)
self.comboBoxExisting.setEditable(True)
for address in addresses:
self.comboBoxExisting.addItem(address)
# self.comboBoxExisting.setObjectName(_fromUtf8("comboBoxExisting"))
self.checkBoxEighteenByteRipe = QtGui.QCheckBox("Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter")
layout = QtGui.QGridLayout()
layout.addWidget(label, 0, 0)
layout.addWidget(labelLabel, 1, 0)
layout.addWidget(self.labelLineEdit, 2, 0)
layout.addWidget(self.radioButtonMostAvailable, 3, 0)
layout.addWidget(self.radioButtonExisting, 4, 0)
layout.addWidget(self.comboBoxExisting, 5, 0)
layout.addWidget(self.checkBoxEighteenByteRipe, 6, 0)
self.setLayout(layout)
QtCore.QObject.connect(self.radioButtonExisting, QtCore.SIGNAL("toggled(bool)"), self.comboBoxExisting.setEnabled)
self.registerField("label", self.labelLineEdit)
self.registerField("radioButtonMostAvailable", self.radioButtonMostAvailable)
self.registerField("radioButtonExisting", self.radioButtonExisting)
self.registerField("comboBoxExisting", self.comboBoxExisting)
# self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage account")
# self.onlyBM = QtGui.QRadioButton("Bitmessage-only account (no email)")
# self.emailAsWell.setChecked(True)
def nextId(self):
return 6
class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("Passphrase")
label = QtGui.QLabel("Deterministric address.")
label.setWordWrap(True)
passphraseLabel = QtGui.QLabel("Passphrase")
self.lineEditPassphrase = QtGui.QLineEdit()
self.lineEditPassphrase.setEchoMode(QtGui.QLineEdit.Password)
self.lineEditPassphrase.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText)
retypePassphraseLabel = QtGui.QLabel("Retype passphrase")
self.lineEditPassphraseAgain = QtGui.QLineEdit()
self.lineEditPassphraseAgain.setEchoMode(QtGui.QLineEdit.Password)
numberLabel = QtGui.QLabel("Number of addresses to make based on your passphrase:")
self.spinBoxNumberOfAddressesToMake = QtGui.QSpinBox()
self.spinBoxNumberOfAddressesToMake.setMinimum(1)
self.spinBoxNumberOfAddressesToMake.setProperty("value", 8)
# self.spinBoxNumberOfAddressesToMake.setObjectName(_fromUtf8("spinBoxNumberOfAddressesToMake"))
label2 = QtGui.QLabel("In addition to your passphrase, you must remember these numbers:")
label3 = QtGui.QLabel("Address version number: 4")
label4 = QtGui.QLabel("Stream number: 1")
layout = QtGui.QGridLayout()
layout.addWidget(label, 0, 0, 1, 4)
layout.addWidget(passphraseLabel, 1, 0, 1, 4)
layout.addWidget(self.lineEditPassphrase, 2, 0, 1, 4)
layout.addWidget(retypePassphraseLabel, 3, 0, 1, 4)
layout.addWidget(self.lineEditPassphraseAgain, 4, 0, 1, 4)
layout.addWidget(numberLabel, 5, 0, 1, 3)
layout.addWidget(self.spinBoxNumberOfAddressesToMake, 5, 3)
layout.setColumnMinimumWidth(3, 1)
layout.addWidget(label2, 6, 0, 1, 4)
layout.addWidget(label3, 7, 0, 1, 2)
layout.addWidget(label4, 7, 2, 1, 2)
self.setLayout(layout)
def nextId(self):
return 6
class NewAddressWizardEmailProviderPage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("Choose email provider")
label = QtGui.QLabel("Currently only Mailchuck email gateway is available "
"(@mailchuck.com email address). In the future, maybe other gateways will be available. "
"Press Next.")
label.setWordWrap(True)
# self.mailchuck = QtGui.QRadioButton("Mailchuck email gateway (@mailchuck.com)")
# self.mailchuck.setChecked(True)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
# layout.addWidget(self.mailchuck)
self.setLayout(layout)
def nextId(self):
return 5
class NewAddressWizardEmailAddressPage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("Email address")
label = QtGui.QLabel("Choosing an email address. Address must end with @mailchuck.com")
label.setWordWrap(True)
self.specificEmail = QtGui.QRadioButton("Pick your own email address:")
self.specificEmail.setChecked(True)
self.emailLineEdit = QtGui.QLineEdit()
self.randomEmail = QtGui.QRadioButton("Generate a random email address")
QtCore.QObject.connect(self.specificEmail, QtCore.SIGNAL("toggled(bool)"), self.emailLineEdit.setEnabled)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
layout.addWidget(self.specificEmail)
layout.addWidget(self.emailLineEdit)
layout.addWidget(self.randomEmail)
self.setLayout(layout)
def nextId(self):
return 6
class NewAddressWizardWaitPage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("Wait")
self.label = QtGui.QLabel("Wait!")
self.label.setWordWrap(True)
self.progressBar = QtGui.QProgressBar()
self.progressBar.setMinimum(0)
self.progressBar.setMaximum(100)
self.progressBar.setValue(0)
# self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage account")
# self.onlyBM = QtGui.QRadioButton("Bitmessage-only account (no email)")
# self.emailAsWell.setChecked(True)
layout = QtGui.QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.progressBar)
# layout.addWidget(self.emailAsWell)
# layout.addWidget(self.onlyBM)
self.setLayout(layout)
def update(self, i):
if i == 101 and self.wizard().currentId() == 6:
self.wizard().button(QtGui.QWizard.NextButton).click()
return
elif i == 101:
print "haha"
return
self.progressBar.setValue(i)
if i == 50:
self.emit(QtCore.SIGNAL('completeChanged()'))
def isComplete(self):
# print "val = " + str(self.progressBar.value())
if self.progressBar.value() >= 50:
return True
else:
return False
def initializePage(self):
if self.field("emailAsWell").toBool():
val = "yes/"
else:
val = "no/"
if self.field("onlyBM").toBool():
val += "yes"
else:
val += "no"
self.label.setText("Wait! " + val)
# self.wizard().button(QtGui.QWizard.NextButton).setEnabled(False)
self.progressBar.setValue(0)
self.thread = NewAddressThread()
self.connect(self.thread, self.thread.signal, self.update)
self.thread.start()
def nextId(self):
return 10
class NewAddressWizardConclusionPage(QtGui.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
self.setTitle("All done!")
label = QtGui.QLabel("You successfully created a new address.")
label.setWordWrap(True)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
class Ui_NewAddressWizard(QtGui.QWizard):
def __init__(self, addresses):
super(QtGui.QWizard, self).__init__()
self.pages = {}
page = NewAddressWizardIntroPage()
self.setPage(0, page)
self.setStartId(0)
page = NewAddressWizardRngPassphrasePage()
self.setPage(1, page)
page = NewAddressWizardRandomPage(addresses)
self.setPage(2, page)
page = NewAddressWizardPassphrasePage()
self.setPage(3, page)
page = NewAddressWizardEmailProviderPage()
self.setPage(4, page)
page = NewAddressWizardEmailAddressPage()
self.setPage(5, page)
page = NewAddressWizardWaitPage()
self.setPage(6, page)
page = NewAddressWizardConclusionPage()
self.setPage(10, page)
self.setWindowTitle("New address wizard")
self.adjustSize()
self.show()
class NewAddressThread(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
self.signal = QtCore.SIGNAL("signal")
def __del__(self):
self.wait()
def createDeterministic():
pass
def createPassphrase():
pass
def broadcastAddress():
pass
def registerMailchuck():
pass
def waitRegistration():
pass
def run(self):
import time
for i in range(1, 101):
time.sleep(0.1) # artificial time delay
self.emit(self.signal, i)
self.emit(self.signal, 101)
# self.terminate()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
wizard = Ui_NewAddressWizard(["a", "b", "c", "d"])
if (wizard.exec_()):
print "Email: " + ("yes" if wizard.field("emailAsWell").toBool() else "no")
print "BM: " + ("yes" if wizard.field("onlyBM").toBool() else "no")
else:
print "Wizard cancelled"
sys.exit()

View File

@ -92,7 +92,7 @@ class Ui_settingsDialog(object):
self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8(""))
self.languageComboBox.setItemText(4, _fromUtf8("Deutsch")) self.languageComboBox.setItemText(4, _fromUtf8("Deutsch"))
self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8(""))
self.languageComboBox.setItemText(5, _fromUtf8("Españl")) self.languageComboBox.setItemText(5, _fromUtf8("Español"))
self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8(""))
self.languageComboBox.setItemText(6, _fromUtf8("русский")) self.languageComboBox.setItemText(6, _fromUtf8("русский"))
self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8(""))
@ -120,15 +120,21 @@ class Ui_settingsDialog(object):
self.groupBox1.setObjectName(_fromUtf8("groupBox1")) self.groupBox1.setObjectName(_fromUtf8("groupBox1"))
self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1) self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1)
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
spacerItem = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) #spacerItem = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_3.addItem(spacerItem, 0, 0, 1, 1) #self.gridLayout_3.addItem(spacerItem, 0, 0, 1, 1)
self.label = QtGui.QLabel(self.groupBox1) self.label = QtGui.QLabel(self.groupBox1)
self.label.setObjectName(_fromUtf8("label")) self.label.setObjectName(_fromUtf8("label"))
self.gridLayout_3.addWidget(self.label, 0, 1, 1, 1) self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1, QtCore.Qt.AlignRight)
self.lineEditTCPPort = QtGui.QLineEdit(self.groupBox1) self.lineEditTCPPort = QtGui.QLineEdit(self.groupBox1)
self.lineEditTCPPort.setMaximumSize(QtCore.QSize(70, 16777215)) self.lineEditTCPPort.setMaximumSize(QtCore.QSize(70, 16777215))
self.lineEditTCPPort.setObjectName(_fromUtf8("lineEditTCPPort")) self.lineEditTCPPort.setObjectName(_fromUtf8("lineEditTCPPort"))
self.gridLayout_3.addWidget(self.lineEditTCPPort, 0, 2, 1, 1) self.gridLayout_3.addWidget(self.lineEditTCPPort, 0, 1, 1, 1, QtCore.Qt.AlignLeft)
self.labelUPnP = QtGui.QLabel(self.groupBox1)
self.labelUPnP.setObjectName(_fromUtf8("labelUPnP"))
self.gridLayout_3.addWidget(self.labelUPnP, 0, 2, 1, 1, QtCore.Qt.AlignRight)
self.checkBoxUPnP = QtGui.QCheckBox(self.groupBox1)
self.checkBoxUPnP.setObjectName(_fromUtf8("checkBoxUPnP"))
self.gridLayout_3.addWidget(self.checkBoxUPnP, 0, 3, 1, 1, QtCore.Qt.AlignLeft)
self.gridLayout_4.addWidget(self.groupBox1, 0, 0, 1, 1) self.gridLayout_4.addWidget(self.groupBox1, 0, 0, 1, 1)
self.groupBox_3 = QtGui.QGroupBox(self.tabNetworkSettings) self.groupBox_3 = QtGui.QGroupBox(self.tabNetworkSettings)
self.groupBox_3.setObjectName(_fromUtf8("groupBox_3")) self.groupBox_3.setObjectName(_fromUtf8("groupBox_3"))
@ -302,6 +308,12 @@ class Ui_settingsDialog(object):
self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1) self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1)
spacerItem8 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) spacerItem8 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_7.addItem(spacerItem8, 3, 1, 1, 1) self.gridLayout_7.addItem(spacerItem8, 3, 1, 1, 1)
self.labelOpenCL = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.labelOpenCL.setObjectName(_fromUtf8("labelOpenCL"))
self.gridLayout_7.addWidget(self.labelOpenCL, 4, 0, 1, 1)
self.checkBoxOpenCL = QtGui.QCheckBox(self.tabMaxAcceptableDifficulty)
self.checkBoxOpenCL.setObjectName = (_fromUtf8("checkBoxOpenCL"))
self.gridLayout_7.addWidget(self.checkBoxOpenCL, 4, 1, 1, 1)
self.tabWidgetSettings.addTab(self.tabMaxAcceptableDifficulty, _fromUtf8("")) self.tabWidgetSettings.addTab(self.tabMaxAcceptableDifficulty, _fromUtf8(""))
self.tabNamecoin = QtGui.QWidget() self.tabNamecoin = QtGui.QWidget()
self.tabNamecoin.setObjectName(_fromUtf8("tabNamecoin")) self.tabNamecoin.setObjectName(_fromUtf8("tabNamecoin"))
@ -449,6 +461,7 @@ class Ui_settingsDialog(object):
self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None))
self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None)) self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None))
self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None)) self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None))
self.labelUPnP.setText(_translate("settingsDialog", "UPnP:", None))
self.groupBox_3.setTitle(_translate("settingsDialog", "Bandwidth limit", None)) self.groupBox_3.setTitle(_translate("settingsDialog", "Bandwidth limit", None))
self.label_24.setText(_translate("settingsDialog", "Maximum download rate (kB/s): [0: unlimited]", None)) self.label_24.setText(_translate("settingsDialog", "Maximum download rate (kB/s): [0: unlimited]", None))
self.label_25.setText(_translate("settingsDialog", "Maximum upload rate (kB/s): [0: unlimited]", None)) self.label_25.setText(_translate("settingsDialog", "Maximum upload rate (kB/s): [0: unlimited]", None))
@ -474,6 +487,7 @@ class Ui_settingsDialog(object):
self.label_13.setText(_translate("settingsDialog", "Maximum acceptable total difficulty:", None)) self.label_13.setText(_translate("settingsDialog", "Maximum acceptable total difficulty:", None))
self.label_14.setText(_translate("settingsDialog", "Maximum acceptable small message difficulty:", None)) self.label_14.setText(_translate("settingsDialog", "Maximum acceptable small message difficulty:", None))
self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabMaxAcceptableDifficulty), _translate("settingsDialog", "Max acceptable difficulty", None)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabMaxAcceptableDifficulty), _translate("settingsDialog", "Max acceptable difficulty", None))
self.labelOpenCL.setText(_translate("settingsDialog", "Hardware GPU acceleration (OpenCL)", None))
self.label_16.setText(_translate("settingsDialog", "<html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=\" font-style:italic;\">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html>", None)) self.label_16.setText(_translate("settingsDialog", "<html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=\" font-style:italic;\">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html>", None))
self.label_17.setText(_translate("settingsDialog", "Host:", None)) self.label_17.setText(_translate("settingsDialog", "Host:", None))
self.label_18.setText(_translate("settingsDialog", "Port:", None)) self.label_18.setText(_translate("settingsDialog", "Port:", None))

View File

@ -153,7 +153,7 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string notr="true" comment="es">Españl</string> <string notr="true" comment="es">Español</string>
</property> </property>
</item> </item>
<item> <item>

View File

@ -0,0 +1,79 @@
#!/usr/bin/python2.7
from PyQt4 import QtCore, QtGui
class SettingsMixin(object):
def warnIfNoObjectName(self):
if self.objectName() == "":
# TODO: logger
pass
def writeState(self, source):
self.warnIfNoObjectName()
settings = QtCore.QSettings()
settings.beginGroup(self.objectName())
settings.setValue("state", source.saveState())
settings.endGroup()
def writeGeometry(self, source):
self.warnIfNoObjectName()
settings = QtCore.QSettings()
settings.beginGroup(self.objectName())
settings.setValue("geometry", source.saveGeometry())
settings.endGroup()
def readGeometry(self, target):
self.warnIfNoObjectName()
settings = QtCore.QSettings()
try:
geom = settings.value("/".join([str(self.objectName()), "geometry"]))
target.restoreGeometry(geom.toByteArray() if hasattr(geom, 'toByteArray') else geom)
except Exception as e:
pass
def readState(self, target):
self.warnIfNoObjectName()
settings = QtCore.QSettings()
try:
state = settings.value("/".join([str(self.objectName()), "state"]))
target.restoreState(state.toByteArray() if hasattr(state, 'toByteArray') else state)
except Exception as e:
pass
class SMainWindow(QtGui.QMainWindow, SettingsMixin):
def loadSettings(self):
self.readGeometry(self)
self.readState(self)
def saveSettings(self):
self.writeState(self)
self.writeGeometry(self)
class STableWidget(QtGui.QTableWidget, SettingsMixin):
def loadSettings(self):
self.readState(self.horizontalHeader())
def saveSettings(self):
self.writeState(self.horizontalHeader())
class SSplitter(QtGui.QSplitter, SettingsMixin):
def loadSettings(self):
self.readState(self)
def saveSettings(self):
self.writeState(self)
class STreeWidget(QtGui.QTreeWidget, SettingsMixin):
def loadSettings(self):
#recurse children
#self.readState(self)
pass
def saveSettings(self):
#recurse children
#self.writeState(self)
pass

106
src/bitmessageqt/utils.py Normal file
View File

@ -0,0 +1,106 @@
from PyQt4 import QtGui
import hashlib
import os
import shared
from addresses import addBMIfNotPresent
str_broadcast_subscribers = '[Broadcast subscribers]'
def identiconize(address):
size = 48
# If you include another identicon library, please generate an
# example identicon with the following md5 hash:
# 3fd4bf901b9d4ea1394f0fb358725b28
try:
identicon_lib = shared.config.get('bitmessagesettings', 'identiconlib')
except:
# default to qidenticon_two_x
identicon_lib = 'qidenticon_two_x'
# As an 'identiconsuffix' you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators. (Note however, that E-Mail programs might convert the BM-address to lowercase first.)
# It can be used as a pseudo-password to salt the generation of the identicons to decrease the risk
# of attacks where someone creates an address to mimic someone else's identicon.
identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix')
if not shared.config.getboolean('bitmessagesettings', 'useidenticons'):
idcon = QtGui.QIcon()
return idcon
if (identicon_lib[:len('qidenticon')] == 'qidenticon'):
# print identicon_lib
# originally by:
# :Author:Shin Adachi <shn@glucose.jp>
# Licesensed under FreeBSD License.
# stripped from PIL and uses QT instead (by sendiulo, same license)
import qidenticon
hash = hashlib.md5(addBMIfNotPresent(address)+identiconsuffix).hexdigest()
use_two_colors = (identicon_lib[:len('qidenticon_two')] == 'qidenticon_two')
opacity = int(not((identicon_lib == 'qidenticon_x') | (identicon_lib == 'qidenticon_two_x') | (identicon_lib == 'qidenticon_b') | (identicon_lib == 'qidenticon_two_b')))*255
penwidth = 0
image = qidenticon.render_identicon(int(hash, 16), size, use_two_colors, opacity, penwidth)
# filename = './images/identicons/'+hash+'.png'
# image.save(filename)
idcon = QtGui.QIcon()
idcon.addPixmap(image, QtGui.QIcon.Normal, QtGui.QIcon.Off)
return idcon
elif identicon_lib == 'pydenticon':
# print identicon_lib
# Here you could load pydenticon.py (just put it in the "src" folder of your Bitmessage source)
from pydenticon import Pydenticon
# It is not included in the source, because it is licensed under GPLv3
# GPLv3 is a copyleft license that would influence our licensing
# Find the source here: http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py
# note that it requires PIL to be installed: http://www.pythonware.com/products/pil/
idcon_render = Pydenticon(addBMIfNotPresent(address)+identiconsuffix, size*3)
rendering = idcon_render._render()
data = rendering.convert("RGBA").tostring("raw", "RGBA")
qim = QImage(data, size, size, QImage.Format_ARGB32)
pix = QPixmap.fromImage(qim)
idcon = QtGui.QIcon()
idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off)
return idcon
def avatarize(address):
"""
loads a supported image for the given address' hash form 'avatars' folder
falls back to default avatar if 'default.*' file exists
falls back to identiconize(address)
"""
idcon = QtGui.QIcon()
hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest()
str_broadcast_subscribers = '[Broadcast subscribers]'
if address == str_broadcast_subscribers:
# don't hash [Broadcast subscribers]
hash = address
# http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats
# print QImageReader.supportedImageFormats ()
# QImageReader.supportedImageFormats ()
extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA']
# try to find a specific avatar
for ext in extensions:
lower_hash = shared.appdata + 'avatars/' + hash + '.' + ext.lower()
upper_hash = shared.appdata + 'avatars/' + hash + '.' + ext.upper()
if os.path.isfile(lower_hash):
# print 'found avatar of ', address
idcon.addFile(lower_hash)
return idcon
elif os.path.isfile(upper_hash):
# print 'found avatar of ', address
idcon.addFile(upper_hash)
return idcon
# if we haven't found any, try to find a default avatar
for ext in extensions:
lower_default = shared.appdata + 'avatars/' + 'default.' + ext.lower()
upper_default = shared.appdata + 'avatars/' + 'default.' + ext.upper()
if os.path.isfile(lower_default):
default = lower_default
idcon.addFile(lower_default)
return idcon
elif os.path.isfile(upper_default):
default = upper_default
idcon.addFile(upper_default)
return idcon
# If no avatar is found
return identiconize(address)

20
src/bitmsghash/Makefile Normal file
View File

@ -0,0 +1,20 @@
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
CCFLAGS += -I/usr/local/Cellar/openssl/1.0.2d_1/include
LDFLAGS += -L/usr/local/Cellar/openssl/1.0.2d_1/lib
endif
all: bitmsghash.so
powtest:
./testpow.py
bitmsghash.so: bitmsghash.o
g++ bitmsghash.o -shared -fPIC -lpthread -lcrypto $(LDFLAGS) -o bitmsghash.so
bitmsghash.o:
g++ -Wall -O3 -march=native -fPIC $(CCFLAGS) -c bitmsghash.cpp
clean:
rm -f bitmsghash.o bitmsghash.so

View File

@ -0,0 +1,276 @@
/*
* This is based on the John The Ripper SHA512 code, modified for double SHA512 and for use as a miner in Bitmessage.
* This software is originally Copyright (c) 2012 Myrice <qqlddg at gmail dot com>
* and it is hereby released to the general public under the following terms:
* Redistribution and use in source and binary forms, with or without modification, are permitted.
*/
#ifdef cl_khr_byte_addressable_store
#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : disable
#endif
#define uint8_t unsigned char
#define uint32_t unsigned int
#define uint64_t unsigned long
#define SALT_SIZE 0
#define BINARY_SIZE 8
#define FULL_BINARY_SIZE 64
#define PLAINTEXT_LENGTH 72
#define CIPHERTEXT_LENGTH 128
/// Warning: This version of SWAP64(n) is slow and avoid bugs on AMD GPUs(7970)
// #define SWAP64(n) as_ulong(as_uchar8(n).s76543210)
#define SWAP64(n) \
(((n) << 56) \
| (((n) & 0xff00) << 40) \
| (((n) & 0xff0000) << 24) \
| (((n) & 0xff000000) << 8) \
| (((n) >> 8) & 0xff000000) \
| (((n) >> 24) & 0xff0000) \
| (((n) >> 40) & 0xff00) \
| ((n) >> 56))
#define rol(x,n) ((x << n) | (x >> (64-n)))
#define ror(x,n) ((x >> n) | (x << (64-n)))
#define Ch(x,y,z) ((x & y) ^ ( (~x) & z))
#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
#define Sigma0(x) ((ror(x,28)) ^ (ror(x,34)) ^ (ror(x,39)))
#define Sigma1(x) ((ror(x,14)) ^ (ror(x,18)) ^ (ror(x,41)))
#define sigma0(x) ((ror(x,1)) ^ (ror(x,8)) ^(x>>7))
#define sigma1(x) ((ror(x,19)) ^ (ror(x,61)) ^(x>>6))
typedef struct { // notice memory align problem
uint64_t H[8];
uint32_t buffer[32]; //1024 bits
uint32_t buflen;
} sha512_ctx;
typedef struct {
uint64_t target;
char v[PLAINTEXT_LENGTH+1];
} sha512_key;
/* Macros for reading/writing chars from int32's */
#define PUTCHAR(buf, index, val) (buf)[(index)>>2] = ((buf)[(index)>>2] & ~(0xffU << (((index) & 3) << 3))) + ((val) << (((index) & 3) << 3))
__constant uint64_t k[] = {
0x428a2f98d728ae22UL, 0x7137449123ef65cdUL, 0xb5c0fbcfec4d3b2fUL,
0xe9b5dba58189dbbcUL,
0x3956c25bf348b538UL, 0x59f111f1b605d019UL, 0x923f82a4af194f9bUL,
0xab1c5ed5da6d8118UL,
0xd807aa98a3030242UL, 0x12835b0145706fbeUL, 0x243185be4ee4b28cUL,
0x550c7dc3d5ffb4e2UL,
0x72be5d74f27b896fUL, 0x80deb1fe3b1696b1UL, 0x9bdc06a725c71235UL,
0xc19bf174cf692694UL,
0xe49b69c19ef14ad2UL, 0xefbe4786384f25e3UL, 0x0fc19dc68b8cd5b5UL,
0x240ca1cc77ac9c65UL,
0x2de92c6f592b0275UL, 0x4a7484aa6ea6e483UL, 0x5cb0a9dcbd41fbd4UL,
0x76f988da831153b5UL,
0x983e5152ee66dfabUL, 0xa831c66d2db43210UL, 0xb00327c898fb213fUL,
0xbf597fc7beef0ee4UL,
0xc6e00bf33da88fc2UL, 0xd5a79147930aa725UL, 0x06ca6351e003826fUL,
0x142929670a0e6e70UL,
0x27b70a8546d22ffcUL, 0x2e1b21385c26c926UL, 0x4d2c6dfc5ac42aedUL,
0x53380d139d95b3dfUL,
0x650a73548baf63deUL, 0x766a0abb3c77b2a8UL, 0x81c2c92e47edaee6UL,
0x92722c851482353bUL,
0xa2bfe8a14cf10364UL, 0xa81a664bbc423001UL, 0xc24b8b70d0f89791UL,
0xc76c51a30654be30UL,
0xd192e819d6ef5218UL, 0xd69906245565a910UL, 0xf40e35855771202aUL,
0x106aa07032bbd1b8UL,
0x19a4c116b8d2d0c8UL, 0x1e376c085141ab53UL, 0x2748774cdf8eeb99UL,
0x34b0bcb5e19b48a8UL,
0x391c0cb3c5c95a63UL, 0x4ed8aa4ae3418acbUL, 0x5b9cca4f7763e373UL,
0x682e6ff3d6b2b8a3UL,
0x748f82ee5defb2fcUL, 0x78a5636f43172f60UL, 0x84c87814a1f0ab72UL,
0x8cc702081a6439ecUL,
0x90befffa23631e28UL, 0xa4506cebde82bde9UL, 0xbef9a3f7b2c67915UL,
0xc67178f2e372532bUL,
0xca273eceea26619cUL, 0xd186b8c721c0c207UL, 0xeada7dd6cde0eb1eUL,
0xf57d4f7fee6ed178UL,
0x06f067aa72176fbaUL, 0x0a637dc5a2c898a6UL, 0x113f9804bef90daeUL,
0x1b710b35131c471bUL,
0x28db77f523047d84UL, 0x32caab7b40c72493UL, 0x3c9ebe0a15c9bebcUL,
0x431d67c49c100d4cUL,
0x4cc5d4becb3e42b6UL, 0x597f299cfc657e2aUL, 0x5fcb6fab3ad6faecUL,
0x6c44198c4a475817UL,
};
static void setup_ctx(sha512_ctx* ctx, const char * password, uint8_t pass_len)
{
uint32_t* b32 = ctx->buffer;
//set password to buffer
for (uint32_t i = 0; i < pass_len; i++) {
PUTCHAR(b32,i,password[i]);
}
ctx->buflen = pass_len;
//append 1 to ctx buffer
uint32_t length = ctx->buflen;
PUTCHAR(b32, length, 0x80);
while((++length & 3) != 0) {
PUTCHAR(b32, length, 0);
}
uint32_t* buffer32 = b32+(length>>2);
for(uint32_t i = length; i < 128; i+=4) {// append 0 to 128
*buffer32++=0;
}
//append length to buffer
uint64_t *buffer64 = (uint64_t *)ctx->buffer;
buffer64[15] = SWAP64(((uint64_t) ctx->buflen) * 8);
}
inline uint64_t sha512(char* password)
{
__private sha512_ctx ctx;
setup_ctx(&ctx, password, 72);
// sha512 main`
int i;
uint64_t a = 0x6a09e667f3bcc908UL;
uint64_t b = 0xbb67ae8584caa73bUL;
uint64_t c = 0x3c6ef372fe94f82bUL;
uint64_t d = 0xa54ff53a5f1d36f1UL;
uint64_t e = 0x510e527fade682d1UL;
uint64_t f = 0x9b05688c2b3e6c1fUL;
uint64_t g = 0x1f83d9abfb41bd6bUL;
uint64_t h = 0x5be0cd19137e2179UL;
__private uint64_t w[16];
uint64_t *data = (uint64_t *) ctx.buffer;
for (i = 0; i < 16; i++)
w[i] = SWAP64(data[i]);
uint64_t t1, t2;
for (i = 0; i < 16; i++) {
t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g);
t2 = Maj(a, b, c) + Sigma0(a);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
for (i = 16; i < 80; i++) {
w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15];
t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g);
t2 = Maj(a, b, c) + Sigma0(a);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
uint64_t finalhash[8];
finalhash[0] = SWAP64(a + 0x6a09e667f3bcc908UL);
finalhash[1] = SWAP64(b + 0xbb67ae8584caa73bUL);
finalhash[2] = SWAP64(c + 0x3c6ef372fe94f82bUL);
finalhash[3] = SWAP64(d + 0xa54ff53a5f1d36f1UL);
finalhash[4] = SWAP64(e + 0x510e527fade682d1UL);
finalhash[5] = SWAP64(f + 0x9b05688c2b3e6c1fUL);
finalhash[6] = SWAP64(g + 0x1f83d9abfb41bd6bUL);
finalhash[7] = SWAP64(h + 0x5be0cd19137e2179UL);
setup_ctx(&ctx, (char*) finalhash, 64);
a = 0x6a09e667f3bcc908UL;
b = 0xbb67ae8584caa73bUL;
c = 0x3c6ef372fe94f82bUL;
d = 0xa54ff53a5f1d36f1UL;
e = 0x510e527fade682d1UL;
f = 0x9b05688c2b3e6c1fUL;
g = 0x1f83d9abfb41bd6bUL;
h = 0x5be0cd19137e2179UL;
data = (uint64_t *) ctx.buffer;
//((uint64_t*)ctx.buffer)[8] = SWAP64((uint64_t)0x80);
for (i = 0; i < 16; i++)
w[i] = SWAP64(data[i]);
for (i = 0; i < 16; i++) {
t1 = k[i] + w[i] + h + Sigma1(e) + Ch(e, f, g);
t2 = Maj(a, b, c) + Sigma0(a);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
for (i = 16; i < 80; i++) {
w[i & 15] =sigma1(w[(i - 2) & 15]) + sigma0(w[(i - 15) & 15]) + w[(i -16) & 15] + w[(i - 7) & 15];
t1 = k[i] + w[i & 15] + h + Sigma1(e) + Ch(e, f, g);
t2 = Maj(a, b, c) + Sigma0(a);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
return SWAP64(a + 0x6a09e667f3bcc908UL);
}
__kernel void kernel_sha512(__global const sha512_key *password,__global uint64_t *hash, uint64_t start)
{
uint64_t idx = get_global_id(0);
if (idx == 0 && start == 0) {
*hash = 0;
}
uint64_t winval;
uint64_t junk[9];
__global uint64_t * source = (__global uint64_t*) password->v;
for (int i = 1; i < 9; i++) {
junk[i] = source[i];
}
junk[0] = SWAP64(idx + (start));
winval = sha512((char*)junk);
if (SWAP64(winval) < password->target) {
*hash = SWAP64(junk[0]);
}
}

View File

@ -0,0 +1,145 @@
// bitmessage cracker, build with g++ or MSVS to a shared library, use included python code for usage under bitmessage
#ifdef _WIN32
#include "Winsock.h"
#include "Windows.h"
#define uint64_t unsigned __int64
#else
#include <arpa/inet.h>
#include <pthread.h>
#include <stdint.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#include "openssl/sha.h"
#define HASH_SIZE 64
#define BUFLEN 16384
#if defined(__GNUC__)
#define EXPORT __attribute__ ((__visibility__("default")))
#elif defined(_WIN32)
#define EXPORT __declspec(dllexport)
#endif
#ifndef __APPLE__
#define ntohll(x) ( ( (uint64_t)(ntohl( (unsigned int)((x << 32) >> 32) )) << 32) | ntohl( ((unsigned int)(x >> 32)) ) )
#endif
unsigned long long max_val;
unsigned char *initialHash;
unsigned long long successval = 0;
unsigned int numthreads = 0;
#ifdef _WIN32
DWORD WINAPI threadfunc(LPVOID param) {
#else
void * threadfunc(void* param) {
#endif
unsigned int incamt = *((unsigned int*)param);
SHA512_CTX sha;
unsigned char buf[HASH_SIZE + sizeof(uint64_t)] = { 0 };
unsigned char output[HASH_SIZE] = { 0 };
memcpy(buf + sizeof(uint64_t), initialHash, HASH_SIZE);
unsigned long long tmpnonce = incamt;
unsigned long long * nonce = (unsigned long long *)buf;
unsigned long long * hash = (unsigned long long *)output;
while (successval == 0) {
tmpnonce += numthreads;
(*nonce) = ntohll(tmpnonce); /* increment nonce */
SHA512_Init(&sha);
SHA512_Update(&sha, buf, HASH_SIZE + sizeof(uint64_t));
SHA512_Final(output, &sha);
SHA512_Init(&sha);
SHA512_Update(&sha, output, HASH_SIZE);
SHA512_Final(output, &sha);
if (ntohll(*hash) < max_val) {
successval = tmpnonce;
}
}
return NULL;
}
void getnumthreads()
{
#ifdef _WIN32
DWORD_PTR dwProcessAffinity, dwSystemAffinity;
#elif __linux__
cpu_set_t dwProcessAffinity;
#else
int dwProcessAffinity = 0;
int32_t core_count = 0;
#endif
size_t len = sizeof(dwProcessAffinity);
if (numthreads > 0)
return;
#ifdef _WIN32
GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity);
#elif __linux__
sched_getaffinity(0, len, &dwProcessAffinity);
#else
if (sysctlbyname("hw.logicalcpu", &core_count, &len, 0, 0) == 0)
numthreads = core_count;
#endif
for (unsigned int i = 0; i < len * 8; i++)
#if defined(_WIN32)
if (dwProcessAffinity & (1i64 << i)) {
#elif defined __linux__
if (CPU_ISSET(i, &dwProcessAffinity)) {
#else
if (dwProcessAffinity & (1 << i)) {
#endif
numthreads++;
printf("Detected core on: %u\n", i);
}
printf("Number of threads: %i\n", (int)numthreads);
}
extern "C" EXPORT unsigned long long BitmessagePOW(unsigned char * starthash, unsigned long long target)
{
successval = 0;
max_val = target;
getnumthreads();
initialHash = (unsigned char *)starthash;
# ifdef _WIN32
HANDLE* threads = (HANDLE*)calloc(sizeof(HANDLE), numthreads);
# else
pthread_t* threads = (pthread_t*)calloc(sizeof(pthread_t), numthreads);
struct sched_param schparam;
schparam.sched_priority = 0;
# endif
unsigned int *threaddata = (unsigned int *)calloc(sizeof(unsigned int), numthreads);
for (unsigned int i = 0; i < numthreads; i++) {
threaddata[i] = i;
# ifdef _WIN32
threads[i] = CreateThread(NULL, 0, threadfunc, (LPVOID)&threaddata[i], 0, NULL);
SetThreadPriority(threads[i], THREAD_PRIORITY_IDLE);
# else
pthread_create(&threads[i], NULL, threadfunc, (void*)&threaddata[i]);
# ifdef __linux__
pthread_setschedparam(threads[i], SCHED_IDLE, &schparam);
# else
pthread_setschedparam(threads[i], SCHED_RR, &schparam);
# endif
# endif
}
# ifdef _WIN32
WaitForMultipleObjects(numthreads, threads, TRUE, INFINITE);
# else
for (unsigned int i = 0; i < numthreads; i++) {
pthread_join(threads[i], NULL);
}
# endif
free(threads);
free(threaddata);
return successval;
}

View File

@ -1,7 +1,8 @@
import os
from setuptools import setup from setuptools import setup
name = "Bitmessage" name = "Bitmessage"
version = "0.4.4" version = os.getenv("PYBITMESSAGEVERSION", "custom")
mainscript = ["bitmessagemain.py"] mainscript = ["bitmessagemain.py"]
setup( setup(
@ -11,7 +12,7 @@ setup(
setup_requires = ["py2app"], setup_requires = ["py2app"],
options = dict( options = dict(
py2app = dict( py2app = dict(
resources = ["images", "translations"], resources = ["images", "translations", "bitmsghash", "sslkeys"],
includes = ['sip', 'PyQt4._qt'], includes = ['sip', 'PyQt4._qt'],
iconfile = "images/bitmessage.icns" iconfile = "images/bitmessage.icns"
) )

View File

@ -8,17 +8,26 @@ import hashlib
import highlevelcrypto import highlevelcrypto
from addresses import * from addresses import *
from debug import logger from debug import logger
from helper_threading import *
from pyelliptic import arithmetic from pyelliptic import arithmetic
import tr import tr
class addressGenerator(threading.Thread): class addressGenerator(threading.Thread, StoppableThread):
def __init__(self): def __init__(self):
# QThread.__init__(self, parent) # QThread.__init__(self, parent)
threading.Thread.__init__(self) threading.Thread.__init__(self, name="addressGenerator")
self.initStop()
def stopThread(self):
try:
shared.addressGeneratorQueue.put(("stopThread", "data"))
except:
pass
super(addressGenerator, self).stopThread()
def run(self): def run(self):
while True: while shared.shutdown == 0:
queueValue = shared.addressGeneratorQueue.get() queueValue = shared.addressGeneratorQueue.get()
nonceTrialsPerByte = 0 nonceTrialsPerByte = 0
payloadLengthExtraBytes = 0 payloadLengthExtraBytes = 0
@ -54,6 +63,8 @@ class addressGenerator(threading.Thread):
numberOfNullBytesDemandedOnFrontOfRipeHash = 2 numberOfNullBytesDemandedOnFrontOfRipeHash = 2
else: else:
numberOfNullBytesDemandedOnFrontOfRipeHash = 1 # the default numberOfNullBytesDemandedOnFrontOfRipeHash = 1 # the default
elif queueValue[0] == 'stopThread':
break
else: else:
sys.stderr.write( sys.stderr.write(
'Programming error: A structure with the wrong number of values was passed into the addressGeneratorQueue. Here is the queueValue: %s\n' % repr(queueValue)) 'Programming error: A structure with the wrong number of values was passed into the addressGeneratorQueue. Here is the queueValue: %s\n' % repr(queueValue))
@ -150,10 +161,8 @@ class addressGenerator(threading.Thread):
sys.stderr.write( sys.stderr.write(
'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.') 'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.')
if command == 'createDeterministicAddresses': if command == 'createDeterministicAddresses':
statusbar = 'Generating ' + str(
numberOfAddressesToMake) + ' new addresses.'
shared.UISignalQueue.put(( shared.UISignalQueue.put((
'updateStatusBar', statusbar)) 'updateStatusBar', tr.translateText("MainWindow","Generating %1 new addresses.").arg(str(numberOfAddressesToMake))))
signingKeyNonce = 0 signingKeyNonce = 0
encryptionKeyNonce = 1 encryptionKeyNonce = 1
listOfNewAddressesToSendOutThroughTheAPI = [ listOfNewAddressesToSendOutThroughTheAPI = [
@ -278,4 +287,4 @@ class addressGenerator(threading.Thread):
else: else:
raise Exception( raise Exception(
"Error in the addressGenerator thread. Thread was given a command it could not understand: " + command) "Error in the addressGenerator thread. Thread was given a command it could not understand: " + command)
shared.addressGeneratorQueue.task_done()

View File

@ -29,7 +29,7 @@ class objectProcessor(threading.Thread):
objecs (msg, broadcast, pubkey, getpubkey) from the receiveDataThreads. objecs (msg, broadcast, pubkey, getpubkey) from the receiveDataThreads.
""" """
def __init__(self): def __init__(self):
threading.Thread.__init__(self) threading.Thread.__init__(self, name="objectProcessor")
""" """
It may be the case that the last time Bitmessage was running, the user It may be the case that the last time Bitmessage was running, the user
closed it before it finished processing everything in the closed it before it finished processing everything in the
@ -741,8 +741,7 @@ class objectProcessor(threading.Thread):
fromAddress = encodeAddress( fromAddress = encodeAddress(
sendersAddressVersion, sendersStream, calculatedRipe) sendersAddressVersion, sendersStream, calculatedRipe)
with shared.printLock: logger.debug('fromAddress: ' + fromAddress)
print 'fromAddress:', fromAddress
if messageEncodingType == 2: if messageEncodingType == 2:
subject, body = self.decodeType2Message(message) subject, body = self.decodeType2Message(message)

View File

@ -2,6 +2,7 @@ import threading
import time import time
import random import random
import shared import shared
import select
import socks import socks
import socket import socket
import sys import sys
@ -9,14 +10,16 @@ import tr
from class_sendDataThread import * from class_sendDataThread import *
from class_receiveDataThread import * from class_receiveDataThread import *
from helper_threading import *
# For each stream to which we connect, several outgoingSynSender threads # For each stream to which we connect, several outgoingSynSender threads
# will exist and will collectively create 8 connections with peers. # will exist and will collectively create 8 connections with peers.
class outgoingSynSender(threading.Thread): class outgoingSynSender(threading.Thread, StoppableThread):
def __init__(self): def __init__(self):
threading.Thread.__init__(self) threading.Thread.__init__(self, name="outgoingSynSender")
self.initStop()
def setup(self, streamNumber, selfInitiatedConnections): def setup(self, streamNumber, selfInitiatedConnections):
self.streamNumber = streamNumber self.streamNumber = streamNumber
@ -35,14 +38,22 @@ class outgoingSynSender(threading.Thread):
shared.knownNodesLock.release() shared.knownNodesLock.release()
return peer return peer
def stopThread(self):
super(outgoingSynSender, self).stopThread()
try:
self.sock.shutdown(socket.SHUT_RDWR)
except:
pass
def run(self): def run(self):
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect') and not self._stopped:
time.sleep(2) self.stop.wait(2)
while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections') and not self._stopped:
self.name = "outgoingSynSender"
maximumConnections = 1 if shared.trustedPeer else 8 # maximum number of outgoing connections = 8 maximumConnections = 1 if shared.trustedPeer else 8 # maximum number of outgoing connections = 8
while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections: while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections:
time.sleep(10) self.stop.wait(10)
if shared.shutdown: if shared.shutdown:
break break
random.seed() random.seed()
@ -53,7 +64,9 @@ class outgoingSynSender(threading.Thread):
# print 'choosing new sample' # print 'choosing new sample'
random.seed() random.seed()
peer = self._getPeer() peer = self._getPeer()
time.sleep(1) self.stop.wait(1)
if shared.shutdown:
break
# Clear out the shared.alreadyAttemptedConnectionsList every half # Clear out the shared.alreadyAttemptedConnectionsList every half
# hour so that this program will again attempt a connection # hour so that this program will again attempt a connection
# to any nodes, even ones it has already tried. # to any nodes, even ones it has already tried.
@ -63,13 +76,17 @@ class outgoingSynSender(threading.Thread):
time.time()) time.time())
shared.alreadyAttemptedConnectionsListLock.acquire() shared.alreadyAttemptedConnectionsListLock.acquire()
shared.alreadyAttemptedConnectionsList[peer] = 0 shared.alreadyAttemptedConnectionsList[peer] = 0
shared.alreadyAttemptedConnectionsListLock.release() try:
shared.alreadyAttemptedConnectionsListLock.release()
except threading.ThreadError as e:
pass
self.name = "outgoingSynSender-" + peer.host
if peer.host.find(':') == -1: if peer.host.find(':') == -1:
address_family = socket.AF_INET address_family = socket.AF_INET
else: else:
address_family = socket.AF_INET6 address_family = socket.AF_INET6
try: try:
sock = socks.socksocket(address_family, socket.SOCK_STREAM) self.sock = socks.socksocket(address_family, socket.SOCK_STREAM)
except: except:
""" """
The line can fail on Windows systems which aren't The line can fail on Windows systems which aren't
@ -86,22 +103,19 @@ class outgoingSynSender(threading.Thread):
except: except:
pass pass
shared.knownNodesLock.release() shared.knownNodesLock.release()
with shared.printLock: logger.debug('deleting ' + str(peer) + ' from shared.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible.')
print 'deleting ', peer, 'from shared.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible.'
continue continue
# This option apparently avoids the TIME_WAIT state so that we # This option apparently avoids the TIME_WAIT state so that we
# can rebind faster # can rebind faster
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(20) self.sock.settimeout(20)
if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2: if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2:
with shared.printLock: logger.debug('Trying an outgoing connection to ' + str(peer))
print 'Trying an outgoing connection to', peer
# sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS4a': elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS4a':
if shared.verbose >= 2: if shared.verbose >= 2:
with shared.printLock: logger.debug ('(Using SOCKS4a) Trying an outgoing connection to ' + str(peer))
print '(Using SOCKS4a) Trying an outgoing connection to', peer
proxytype = socks.PROXY_TYPE_SOCKS4 proxytype = socks.PROXY_TYPE_SOCKS4
sockshostname = shared.config.get( sockshostname = shared.config.get(
@ -114,15 +128,14 @@ class outgoingSynSender(threading.Thread):
'bitmessagesettings', 'socksusername') 'bitmessagesettings', 'socksusername')
sockspassword = shared.config.get( sockspassword = shared.config.get(
'bitmessagesettings', 'sockspassword') 'bitmessagesettings', 'sockspassword')
sock.setproxy( self.sock.setproxy(
proxytype, sockshostname, socksport, rdns, socksusername, sockspassword) proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
else: else:
sock.setproxy( self.sock.setproxy(
proxytype, sockshostname, socksport, rdns) proxytype, sockshostname, socksport, rdns)
elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5': elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
if shared.verbose >= 2: if shared.verbose >= 2:
with shared.printLock: logger.debug ('(Using SOCKS5) Trying an outgoing connection to ' + str(peer))
print '(Using SOCKS5) Trying an outgoing connection to', peer
proxytype = socks.PROXY_TYPE_SOCKS5 proxytype = socks.PROXY_TYPE_SOCKS5
sockshostname = shared.config.get( sockshostname = shared.config.get(
@ -135,19 +148,19 @@ class outgoingSynSender(threading.Thread):
'bitmessagesettings', 'socksusername') 'bitmessagesettings', 'socksusername')
sockspassword = shared.config.get( sockspassword = shared.config.get(
'bitmessagesettings', 'sockspassword') 'bitmessagesettings', 'sockspassword')
sock.setproxy( self.sock.setproxy(
proxytype, sockshostname, socksport, rdns, socksusername, sockspassword) proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
else: else:
sock.setproxy( self.sock.setproxy(
proxytype, sockshostname, socksport, rdns) proxytype, sockshostname, socksport, rdns)
try: try:
sock.connect((peer.host, peer.port)) self.sock.connect((peer.host, peer.port))
rd = receiveDataThread() rd = receiveDataThread()
rd.daemon = True # close the main program even if there are threads left 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. someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory.
sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection. sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection.
rd.setup(sock, rd.setup(self.sock,
peer.host, peer.host,
peer.port, peer.port,
self.streamNumber, self.streamNumber,
@ -155,20 +168,18 @@ class outgoingSynSender(threading.Thread):
self.selfInitiatedConnections, self.selfInitiatedConnections,
sendDataThreadQueue) sendDataThreadQueue)
rd.start() rd.start()
with shared.printLock: logger.debug(str(self) + ' connected to ' + str(peer) + ' during an outgoing attempt.')
print self, 'connected to', peer, 'during an outgoing attempt.'
sd = sendDataThread(sendDataThreadQueue) sd = sendDataThread(sendDataThreadQueue)
sd.setup(sock, peer.host, peer.port, self.streamNumber, sd.setup(self.sock, peer.host, peer.port, self.streamNumber,
someObjectsOfWhichThisRemoteNodeIsAlreadyAware) someObjectsOfWhichThisRemoteNodeIsAlreadyAware)
sd.start() sd.start()
sd.sendVersionMessage() sd.sendVersionMessage()
except socks.GeneralProxyError as err: except socks.GeneralProxyError as err:
if shared.verbose >= 2: if shared.verbose >= 2:
with shared.printLock: logger.debug('Could NOT connect to ' + str(peer) + ' during outgoing attempt. ' + str(err))
print 'Could NOT connect to', peer, 'during outgoing attempt.', err
deletedPeer = None deletedPeer = None
with shared.knownNodesLock: with shared.knownNodesLock:
@ -186,8 +197,7 @@ class outgoingSynSender(threading.Thread):
del shared.knownNodes[self.streamNumber][peer] del shared.knownNodes[self.streamNumber][peer]
deletedPeer = peer deletedPeer = peer
if deletedPeer: if deletedPeer:
with shared.printLock: str ('deleting ' + str(peer) + ' 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: except socks.Socks5AuthError as err:
shared.UISignalQueue.put(( shared.UISignalQueue.put((
@ -195,16 +205,15 @@ class outgoingSynSender(threading.Thread):
"MainWindow", "SOCKS5 Authentication problem: %1").arg(str(err)))) "MainWindow", "SOCKS5 Authentication problem: %1").arg(str(err))))
except socks.Socks5Error as err: except socks.Socks5Error as err:
pass pass
print 'SOCKS5 error. (It is possible that the server wants authentication).)', str(err) logger.error('SOCKS5 error. (It is possible that the server wants authentication).) ' + str(err))
except socks.Socks4Error as err: except socks.Socks4Error as err:
print 'Socks4Error:', err logger.error('Socks4Error: ' + str(err))
except socket.error as err: except socket.error as err:
if shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS': if shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS':
print 'Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str(err) logger.error('Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str(err))
else: else:
if shared.verbose >= 1: if shared.verbose >= 1:
with shared.printLock: logger.debug('Could NOT connect to ' + str(peer) + 'during outgoing attempt. ' + str(err))
print 'Could NOT connect to', peer, 'during outgoing attempt.', err
deletedPeer = None deletedPeer = None
with shared.knownNodesLock: with shared.knownNodesLock:
@ -222,12 +231,9 @@ class outgoingSynSender(threading.Thread):
del shared.knownNodes[self.streamNumber][peer] del shared.knownNodes[self.streamNumber][peer]
deletedPeer = peer deletedPeer = peer
if deletedPeer: if deletedPeer:
with shared.printLock: logger.debug('deleting ' + str(peer) + ' 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 Exception as err: except Exception as err:
sys.stderr.write(
'An exception has occurred in the outgoingSynSender thread that was not caught by other exception types: ')
import traceback import traceback
traceback.print_exc() logger.exception('An exception has occurred in the outgoingSynSender thread that was not caught by other exception types:')
time.sleep(0.1) self.stop.wait(0.1)

View File

@ -1,11 +1,15 @@
doTimingAttackMitigation = True doTimingAttackMitigation = True
import errno
import time import time
import threading import threading
import shared import shared
import hashlib import hashlib
import os
import select
import socket import socket
import random import random
import ssl
from struct import unpack, pack from struct import unpack, pack
import sys import sys
import traceback import traceback
@ -25,7 +29,7 @@ from debug import logger
class receiveDataThread(threading.Thread): class receiveDataThread(threading.Thread):
def __init__(self): def __init__(self):
threading.Thread.__init__(self) threading.Thread.__init__(self, name="receiveData")
self.data = '' self.data = ''
self.verackSent = False self.verackSent = False
self.verackReceived = False self.verackReceived = False
@ -42,6 +46,7 @@ class receiveDataThread(threading.Thread):
self.sock = sock self.sock = sock
self.peer = shared.Peer(HOST, port) self.peer = shared.Peer(HOST, port)
self.name = "receiveData-" + self.peer.host
self.streamNumber = streamNumber self.streamNumber = streamNumber
self.objectsThatWeHaveYetToGetFromThisPeer = {} self.objectsThatWeHaveYetToGetFromThisPeer = {}
self.selfInitiatedConnections = selfInitiatedConnections self.selfInitiatedConnections = selfInitiatedConnections
@ -49,6 +54,7 @@ class receiveDataThread(threading.Thread):
shared.connectedHostsList[ shared.connectedHostsList[
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.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. 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.
self.services = 0
if self.streamNumber == -1: # This was an incoming connection. Send out a version message if we accept the other node's version message. 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 self.initiatedConnection = False
else: else:
@ -57,8 +63,7 @@ class receiveDataThread(threading.Thread):
self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware
def run(self): def run(self):
with shared.printLock: logger.debug('receiveDataThread starting. ID ' + str(id(self)) + '. The size of the shared.connectedHostsList is now ' + str(len(shared.connectedHostsList)))
print 'receiveDataThread starting. ID', str(id(self)) + '. The size of the shared.connectedHostsList is now', len(shared.connectedHostsList)
while True: while True:
if shared.config.getint('bitmessagesettings', 'maxdownloadrate') == 0: if shared.config.getint('bitmessagesettings', 'maxdownloadrate') == 0:
@ -76,38 +81,41 @@ class receiveDataThread(threading.Thread):
shared.numberOfBytesReceivedLastSecond = 0 shared.numberOfBytesReceivedLastSecond = 0
dataLen = len(self.data) dataLen = len(self.data)
try: try:
dataRecv = self.sock.recv(1024) if ((self.services & shared.NODE_SSL == shared.NODE_SSL) and
self.connectionIsOrWasFullyEstablished and
shared.haveSSL(not self.initiatedConnection)):
dataRecv = self.sslSock.recv(1024)
else:
dataRecv = self.sock.recv(1024)
self.data += dataRecv self.data += dataRecv
shared.numberOfBytesReceived += len(dataRecv) # for the 'network status' UI tab. The UI clears this value whenever it updates. shared.numberOfBytesReceived += len(dataRecv) # for the 'network status' UI tab. The UI clears this value whenever it updates.
shared.numberOfBytesReceivedLastSecond += len(dataRecv) # for the download rate limit shared.numberOfBytesReceivedLastSecond += len(dataRecv) # for the download rate limit
except socket.timeout: except socket.timeout:
with shared.printLock: logger.error ('Timeout occurred waiting for data from ' + str(self.peer) + '. Closing receiveData thread. (ID: ' + str(id(self)) + ')')
print 'Timeout occurred waiting for data from', self.peer, '. Closing receiveData thread. (ID:', str(id(self)) + ')'
break break
except Exception as err: except Exception as err:
with shared.printLock: if (sys.platform == 'win32' and err.errno in ([2, 10035])) or (sys.platform != 'win32' and err.errno == errno.EWOULDBLOCK):
print 'sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID:', str(id(self)) + ').', err select.select([self.sslSock], [], [])
continue
logger.error('sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID: ' + str(id(self)) + ').' + str(err.errno) + "/" + str(err))
break break
# print 'Received', repr(self.data) # print 'Received', repr(self.data)
if len(self.data) == dataLen: # If self.sock.recv returned no data: if len(self.data) == dataLen: # If self.sock.recv returned no data:
with shared.printLock: logger.debug('Connection to ' + str(self.peer) + ' closed. Closing receiveData thread. (ID: ' + str(id(self)) + ')')
print 'Connection to', self.peer, 'closed. Closing receiveData thread. (ID:', str(id(self)) + ')'
break break
else: else:
self.processData() self.processData()
try: try:
del self.selfInitiatedConnections[self.streamNumber][self] del self.selfInitiatedConnections[self.streamNumber][self]
with shared.printLock: logger.debug('removed self (a receiveDataThread) from selfInitiatedConnections')
print 'removed self (a receiveDataThread) from selfInitiatedConnections'
except: except:
pass pass
self.sendDataThreadQueue.put((0, 'shutdown','no data')) # commands the corresponding sendDataThread to shut itself down. self.sendDataThreadQueue.put((0, 'shutdown','no data')) # commands the corresponding sendDataThread to shut itself down.
try: try:
del shared.connectedHostsList[self.peer.host] del shared.connectedHostsList[self.peer.host]
except Exception as err: except Exception as err:
with shared.printLock: logger.error('Could not delete ' + str(self.peer.host) + ' from shared.connectedHostsList.' + str(err))
print 'Could not delete', self.peer.host, 'from shared.connectedHostsList.', err
try: try:
del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[
@ -115,8 +123,7 @@ class receiveDataThread(threading.Thread):
except: except:
pass pass
shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data')) shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data'))
with shared.printLock: logger.debug('receiveDataThread ending. ID ' + str(id(self)) + '. The size of the shared.connectedHostsList is now ' + str(len(shared.connectedHostsList)))
print 'receiveDataThread ending. ID', str(id(self)) + '. The size of the shared.connectedHostsList is now', len(shared.connectedHostsList)
def processData(self): def processData(self):
@ -137,7 +144,7 @@ class receiveDataThread(threading.Thread):
return return
payload = self.data[shared.Header.size:payloadLength + shared.Header.size] payload = self.data[shared.Header.size:payloadLength + shared.Header.size]
if checksum != hashlib.sha512(payload).digest()[0:4]: # test the checksum in the message. if checksum != hashlib.sha512(payload).digest()[0:4]: # test the checksum in the message.
print 'Checksum incorrect. Clearing this message.' logger.error('Checksum incorrect. Clearing this message.')
self.data = self.data[payloadLength + shared.Header.size:] self.data = self.data[payloadLength + shared.Header.size:]
del magic,command,payloadLength,checksum,payload # better to clean up before the recursive call del magic,command,payloadLength,checksum,payload # better to clean up before the recursive call
self.processData() self.processData()
@ -152,8 +159,7 @@ class receiveDataThread(threading.Thread):
#Strip the nulls #Strip the nulls
command = command.rstrip('\x00') command = command.rstrip('\x00')
with shared.printLock: logger.debug('remoteCommand ' + repr(command) + ' from ' + str(self.peer))
print 'remoteCommand', repr(command), ' from', self.peer
try: try:
#TODO: Use a dispatcher here #TODO: Use a dispatcher here
@ -191,14 +197,12 @@ class receiveDataThread(threading.Thread):
objectHash, = random.sample( objectHash, = random.sample(
self.objectsThatWeHaveYetToGetFromThisPeer, 1) self.objectsThatWeHaveYetToGetFromThisPeer, 1)
if objectHash in shared.inventory: if objectHash in shared.inventory:
with shared.printLock: logger.debug('Inventory (in memory) already has object listed in inv message.')
print 'Inventory (in memory) already has object listed in inv message.'
del self.objectsThatWeHaveYetToGetFromThisPeer[ del self.objectsThatWeHaveYetToGetFromThisPeer[
objectHash] objectHash]
elif shared.isInSqlInventory(objectHash): elif shared.isInSqlInventory(objectHash):
if shared.verbose >= 3: if shared.verbose >= 3:
with shared.printLock: logger.debug('Inventory (SQL on disk) already has object listed in inv message.')
print 'Inventory (SQL on disk) already has object listed in inv message.'
del self.objectsThatWeHaveYetToGetFromThisPeer[ del self.objectsThatWeHaveYetToGetFromThisPeer[
objectHash] objectHash]
else: else:
@ -207,8 +211,7 @@ class receiveDataThread(threading.Thread):
del self.objectsThatWeHaveYetToGetFromThisPeer[ del self.objectsThatWeHaveYetToGetFromThisPeer[
objectHash] # It is possible that the remote node might not respond with the object. In that case, we'll very likely get it from someone else anyway. objectHash] # It is possible that the remote node might not respond with the object. In that case, we'll very likely get it from someone else anyway.
if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0:
with shared.printLock: logger.debug('(concerning' + str(self.peer) + ') number of objectsThatWeHaveYetToGetFromThisPeer is now 0')
print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now 0'
try: try:
del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[
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. 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.
@ -217,16 +220,14 @@ class receiveDataThread(threading.Thread):
break break
if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0:
# We had objectsThatWeHaveYetToGetFromThisPeer but the loop ran, they were all in our inventory, and now we don't have any to get anymore. # We had objectsThatWeHaveYetToGetFromThisPeer but the loop ran, they were all in our inventory, and now we don't have any to get anymore.
with shared.printLock: logger.debug('(concerning' + str(self.peer) + ') number of objectsThatWeHaveYetToGetFromThisPeer is now 0')
print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now 0'
try: try:
del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[ del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[
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. 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: except:
pass pass
if len(self.objectsThatWeHaveYetToGetFromThisPeer) > 0: if len(self.objectsThatWeHaveYetToGetFromThisPeer) > 0:
with shared.printLock: logger.debug('(concerning' + str(self.peer) + ') number of objectsThatWeHaveYetToGetFromThisPeer is now ' + str(len(self.objectsThatWeHaveYetToGetFromThisPeer)))
print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer)
shared.numberOfObjectsThatWeHaveYetToGetPerPeer[self.peer] = len( shared.numberOfObjectsThatWeHaveYetToGetPerPeer[self.peer] = len(
self.objectsThatWeHaveYetToGetFromThisPeer) # 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.objectsThatWeHaveYetToGetFromThisPeer) # 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.
@ -234,14 +235,12 @@ class receiveDataThread(threading.Thread):
def sendpong(self): def sendpong(self):
with shared.printLock: logger.debug('Sending pong')
print 'Sending pong'
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('pong'))) self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('pong')))
def recverack(self): def recverack(self):
with shared.printLock: logger.debug('verack received')
print 'verack received'
self.verackReceived = True self.verackReceived = True
if self.verackSent: if self.verackSent:
# We have thus both sent and received a verack. # We have thus both sent and received a verack.
@ -252,19 +251,38 @@ class receiveDataThread(threading.Thread):
# there is no reason to run this function a second time # there is no reason to run this function a second time
return return
self.connectionIsOrWasFullyEstablished = True self.connectionIsOrWasFullyEstablished = True
self.sslSock = self.sock
if ((self.services & shared.NODE_SSL == shared.NODE_SSL) and
shared.haveSSL(not self.initiatedConnection)):
logger.debug("Initialising TLS")
self.sslSock = ssl.wrap_socket(self.sock, keyfile = os.path.join(shared.codePath(), 'sslkeys', 'key.pem'), certfile = os.path.join(shared.codePath(), 'sslkeys', 'cert.pem'), server_side = not self.initiatedConnection, ssl_version=ssl.PROTOCOL_TLSv1, do_handshake_on_connect=False, ciphers='AECDH-AES256-SHA')
if hasattr(self.sslSock, "context"):
self.sslSock.context.set_ecdh_curve("secp256k1")
while True:
try:
self.sslSock.do_handshake()
break
except ssl.SSLError as e:
if e.errno == 2:
select.select([self.sslSock], [self.sslSock], [])
else:
break
except:
break
# Command the corresponding sendDataThread to set its own connectionIsOrWasFullyEstablished variable to True also # Command the corresponding sendDataThread to set its own connectionIsOrWasFullyEstablished variable to True also
self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', 'no data')) self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', (self.services, self.sslSock)))
if not self.initiatedConnection: if not self.initiatedConnection:
shared.clientHasReceivedIncomingConnections = True shared.clientHasReceivedIncomingConnections = True
shared.UISignalQueue.put(('setStatusIcon', 'green')) shared.UISignalQueue.put(('setStatusIcon', 'green'))
self.sock.settimeout( 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. 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')) shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data'))
with shared.printLock: logger.debug('Connection fully established with ' + str(self.peer) + "\n" + \
print 'Connection fully established with', self.peer 'The size of the connectedHostsList is now ' + str(len(shared.connectedHostsList)) + "\n" + \
print 'The size of the connectedHostsList is now', len(shared.connectedHostsList) 'The length of sendDataQueues is now: ' + str(len(shared.sendDataQueues)) + "\n" + \
print 'The length of sendDataQueues is now:', len(shared.sendDataQueues) 'broadcasting addr from within connectionFullyEstablished function.')
print 'broadcasting addr from within connectionFullyEstablished function.'
# Let all of our peers know about this new node. # Let all of our peers know about this new node.
dataToSend = (int(time.time()), self.streamNumber, 1, self.peer.host, self.remoteNodeIncomingPort) dataToSend = (int(time.time()), self.streamNumber, 1, self.peer.host, self.remoteNodeIncomingPort)
@ -273,8 +291,7 @@ class receiveDataThread(threading.Thread):
self.sendaddr() # This is one large addr message to this one peer. self.sendaddr() # This is one large addr message to this one peer.
if not self.initiatedConnection and len(shared.connectedHostsList) > 200: if not self.initiatedConnection and len(shared.connectedHostsList) > 200:
with shared.printLock: logger.info ('We are connected to too many people. Closing connection.')
print 'We are connected to too many people. Closing connection.'
self.sendDataThreadQueue.put((0, 'shutdown','no data')) self.sendDataThreadQueue.put((0, 'shutdown','no data'))
return return
@ -320,8 +337,7 @@ class receiveDataThread(threading.Thread):
# function for broadcasting invs to everyone in our stream. # function for broadcasting invs to everyone in our stream.
def sendinvMessageToJustThisOnePeer(self, numberOfObjects, payload): def sendinvMessageToJustThisOnePeer(self, numberOfObjects, payload):
payload = encodeVarint(numberOfObjects) + payload payload = encodeVarint(numberOfObjects) + payload
with shared.printLock: logger.debug('Sending huge inv message with ' + str(numberOfObjects) + ' objects to just this one peer')
print 'Sending huge inv message with', numberOfObjects, 'objects to just this one peer'
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('inv', payload))) self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('inv', payload)))
def _sleepForTimingAttackMitigation(self, sleepTime): def _sleepForTimingAttackMitigation(self, sleepTime):
@ -329,8 +345,7 @@ class receiveDataThread(threading.Thread):
# only connected to the trusted peer because we can trust the # only connected to the trusted peer because we can trust the
# peer not to attack # peer not to attack
if sleepTime > 0 and doTimingAttackMitigation and shared.trustedPeer == None: if sleepTime > 0 and doTimingAttackMitigation and shared.trustedPeer == None:
with shared.printLock: logger.debug('Timing attack mitigation: Sleeping for ' + str(sleepTime) + ' seconds.')
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
time.sleep(sleepTime) time.sleep(sleepTime)
def recerror(self, data): def recerror(self, data):
@ -388,30 +403,27 @@ class receiveDataThread(threading.Thread):
if len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer) > 0: if len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer) > 0:
for key, value in shared.numberOfObjectsThatWeHaveYetToGetPerPeer.items(): for key, value in shared.numberOfObjectsThatWeHaveYetToGetPerPeer.items():
totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers += value totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers += value
with shared.printLock: logger.debug('number of keys(hosts) in shared.numberOfObjectsThatWeHaveYetToGetPerPeer: ' + str(len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)) + "\n" + \
print 'number of keys(hosts) in shared.numberOfObjectsThatWeHaveYetToGetPerPeer:', len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer) 'totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers = ' + str(totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers))
print 'totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers = ', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers
numberOfItemsInInv, lengthOfVarint = decodeVarint(data[:10]) numberOfItemsInInv, lengthOfVarint = decodeVarint(data[:10])
if numberOfItemsInInv > 50000: if numberOfItemsInInv > 50000:
sys.stderr.write('Too many items in inv message!') sys.stderr.write('Too many items in inv message!')
return return
if len(data) < lengthOfVarint + (numberOfItemsInInv * 32): if len(data) < lengthOfVarint + (numberOfItemsInInv * 32):
print 'inv message doesn\'t contain enough data. Ignoring.' logger.info('inv message doesn\'t contain enough data. Ignoring.')
return return
if numberOfItemsInInv == 1: # we'll just request this data from the person who advertised the object. if numberOfItemsInInv == 1: # we'll just request this data from the person who advertised the object.
if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000 and shared.trustedPeer == None: # inv flooding attack mitigation
with shared.printLock: logger.debug('We already have ' + str(totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers) + ' items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.')
print 'We already have', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers, 'items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.'
return return
self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[ self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[
data[lengthOfVarint:32 + lengthOfVarint]] = 0 data[lengthOfVarint:32 + lengthOfVarint]] = 0
shared.numberOfInventoryLookupsPerformed += 1 shared.numberOfInventoryLookupsPerformed += 1
if data[lengthOfVarint:32 + lengthOfVarint] in shared.inventory: if data[lengthOfVarint:32 + lengthOfVarint] in shared.inventory:
with shared.printLock: logger.debug('Inventory (in memory) has inventory item already.')
print 'Inventory (in memory) has inventory item already.'
elif shared.isInSqlInventory(data[lengthOfVarint:32 + lengthOfVarint]): elif shared.isInSqlInventory(data[lengthOfVarint:32 + lengthOfVarint]):
print 'Inventory (SQL on disk) has inventory item already.' logger.debug('Inventory (SQL on disk) has inventory item already.')
else: else:
self.sendgetdata(data[lengthOfVarint:32 + lengthOfVarint]) self.sendgetdata(data[lengthOfVarint:32 + lengthOfVarint])
else: else:
@ -425,9 +437,8 @@ class receiveDataThread(threading.Thread):
objectsNewToMe = advertisedSet - shared.inventorySets[self.streamNumber] objectsNewToMe = advertisedSet - shared.inventorySets[self.streamNumber]
logger.info('inv message lists %s objects. Of those %s are new to me. It took %s seconds to figure that out.', numberOfItemsInInv, len(objectsNewToMe), time.time()-startTime) logger.info('inv message lists %s objects. Of those %s are new to me. It took %s seconds to figure that out.', numberOfItemsInInv, len(objectsNewToMe), time.time()-startTime)
for item in objectsNewToMe: for item in objectsNewToMe:
if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000 and shared.trustedPeer == None: # inv flooding attack mitigation
with shared.printLock: logger.debug('We already have ' + str(totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers) + ' items yet to retrieve from peers and over ' + str(len(self.objectsThatWeHaveYetToGetFromThisPeer)), ' from this node in particular. Ignoring the rest of this inv message.')
print 'We already have', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers, 'items yet to retrieve from peers and over', len(self.objectsThatWeHaveYetToGetFromThisPeer), 'from this node in particular. Ignoring the rest of this inv message.'
break break
self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[item] = 0 # helps us keep from sending inv messages to peers that already know about the objects listed therein self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[item] = 0 # helps us keep from sending inv messages to peers that already know about the objects listed therein
self.objectsThatWeHaveYetToGetFromThisPeer[item] = 0 # upon finishing dealing with an incoming message, the receiveDataThread will request a random object of from peer out of this data structure. This way if we get multiple inv messages from multiple peers which list mostly the same objects, we will make getdata requests for different random objects from the various peers. self.objectsThatWeHaveYetToGetFromThisPeer[item] = 0 # upon finishing dealing with an incoming message, the receiveDataThread will request a random object of from peer out of this data structure. This way if we get multiple inv messages from multiple peers which list mostly the same objects, we will make getdata requests for different random objects from the various peers.
@ -438,8 +449,7 @@ class receiveDataThread(threading.Thread):
# Send a getdata message to our peer to request the object with the given # Send a getdata message to our peer to request the object with the given
# hash # hash
def sendgetdata(self, hash): def sendgetdata(self, hash):
with shared.printLock: logger.debug('sending getdata to retrieve object with hash: ' + hash.encode('hex'))
print 'sending getdata to retrieve object with hash:', hash.encode('hex')
payload = '\x01' + hash payload = '\x01' + hash
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('getdata', payload))) self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('getdata', payload)))
@ -449,13 +459,12 @@ class receiveDataThread(threading.Thread):
numberOfRequestedInventoryItems, lengthOfVarint = decodeVarint( numberOfRequestedInventoryItems, lengthOfVarint = decodeVarint(
data[:10]) data[:10])
if len(data) < lengthOfVarint + (32 * numberOfRequestedInventoryItems): if len(data) < lengthOfVarint + (32 * numberOfRequestedInventoryItems):
print 'getdata message does not contain enough data. Ignoring.' logger.debug('getdata message does not contain enough data. Ignoring.')
return return
for i in xrange(numberOfRequestedInventoryItems): for i in xrange(numberOfRequestedInventoryItems):
hash = data[lengthOfVarint + ( hash = data[lengthOfVarint + (
i * 32):32 + lengthOfVarint + (i * 32)] i * 32):32 + lengthOfVarint + (i * 32)]
with shared.printLock: logger.debug('received getdata request for item:' + hash.encode('hex'))
print 'received getdata request for item:', hash.encode('hex')
shared.numberOfInventoryLookupsPerformed += 1 shared.numberOfInventoryLookupsPerformed += 1
shared.inventoryLock.acquire() shared.inventoryLock.acquire()
@ -478,33 +487,35 @@ class receiveDataThread(threading.Thread):
# Our peer has requested (in a getdata message) that we send an object. # Our peer has requested (in a getdata message) that we send an object.
def sendObject(self, payload): def sendObject(self, payload):
with shared.printLock: logger.debug('sending an object.')
print 'sending an object.'
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('object',payload))) self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('object',payload)))
def _checkIPv4Address(self, host, hostStandardFormat): def _checkIPv4Address(self, host, hostStandardFormat):
# print 'hostStandardFormat', hostStandardFormat # print 'hostStandardFormat', hostStandardFormat
if host[0] == '\x7F': if host[0] == '\x7F': # 127/8
print 'Ignoring IP address in loopback range:', hostStandardFormat logger.debug('Ignoring IP address in loopback range: ' + hostStandardFormat)
return False return False
if host[0] == '\x0A': if host[0] == '\x0A': # 10/8
print 'Ignoring IP address in private range:', hostStandardFormat logger.debug('Ignoring IP address in private range: ' + hostStandardFormat)
return False return False
if host[0:2] == '\xC0\xA8': if host[0:2] == '\xC0\xA8': # 192.168/16
print 'Ignoring IP address in private range:', hostStandardFormat logger.debug('Ignoring IP address in private range: ' + hostStandardFormat)
return False
if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12
logger.debug('Ignoring IP address in private range:' + hostStandardFormat)
return False return False
return True return True
def _checkIPv6Address(self, host, hostStandardFormat): def _checkIPv6Address(self, host, hostStandardFormat):
if host == ('\x00' * 15) + '\x01': if host == ('\x00' * 15) + '\x01':
print 'Ignoring loopback address:', hostStandardFormat logger.debug('Ignoring loopback address: ' + hostStandardFormat)
return False return False
if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80: if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80:
print 'Ignoring local address:', hostStandardFormat logger.debug ('Ignoring local address: ' + hostStandardFormat)
return False return False
if (ord(host[0]) & 0xfe) == 0xfc: if (ord(host[0]) & 0xfe) == 0xfc:
print 'Ignoring unique local address:', hostStandardFormat logger.debug ('Ignoring unique local address: ' + hostStandardFormat)
return False return False
return True return True
@ -514,13 +525,12 @@ class receiveDataThread(threading.Thread):
data[:10]) data[:10])
if shared.verbose >= 1: if shared.verbose >= 1:
with shared.printLock: logger.debug('addr message contains ' + str(numberOfAddressesIncluded) + ' IP addresses.')
print 'addr message contains', numberOfAddressesIncluded, 'IP addresses.'
if numberOfAddressesIncluded > 1000 or numberOfAddressesIncluded == 0: if numberOfAddressesIncluded > 1000 or numberOfAddressesIncluded == 0:
return return
if len(data) != lengthOfNumberOfAddresses + (38 * numberOfAddressesIncluded): if len(data) != lengthOfNumberOfAddresses + (38 * numberOfAddressesIncluded):
print 'addr message does not contain the correct amount of data. Ignoring.' logger.debug('addr message does not contain the correct amount of data. Ignoring.')
return return
for i in range(0, numberOfAddressesIncluded): for i in range(0, numberOfAddressesIncluded):
@ -558,8 +568,7 @@ class receiveDataThread(threading.Thread):
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. 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.
with shared.knownNodesLock: with shared.knownNodesLock:
shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode
with shared.printLock: logger.debug('added new node ' + str(peerFromAddrMessage) + ' to knownNodes in stream ' + str(recaddrStream))
print 'added new node', peerFromAddrMessage, 'to knownNodes in stream', recaddrStream
shared.needToWriteKnownNodesToDisk = True shared.needToWriteKnownNodesToDisk = True
hostDetails = ( hostDetails = (
@ -574,8 +583,7 @@ class receiveDataThread(threading.Thread):
with shared.knownNodesLock: with shared.knownNodesLock:
shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode
with shared.printLock: logger.debug('knownNodes currently has ' + str(len(shared.knownNodes[self.streamNumber])) + ' nodes for this stream.')
print 'knownNodes currently has', len(shared.knownNodes[self.streamNumber]), 'nodes for this stream.'
# Send a huge addr message to our peer. This is only used # Send a huge addr message to our peer. This is only used
@ -671,10 +679,10 @@ class receiveDataThread(threading.Thread):
""" """
return return
self.remoteProtocolVersion, = unpack('>L', data[:4]) self.remoteProtocolVersion, = unpack('>L', data[:4])
self.services, = unpack('>q', data[4:12])
if self.remoteProtocolVersion < 3: if self.remoteProtocolVersion < 3:
self.sendDataThreadQueue.put((0, 'shutdown','no data')) self.sendDataThreadQueue.put((0, 'shutdown','no data'))
with shared.printLock: logger.debug ('Closing connection to old protocol version ' + str(self.remoteProtocolVersion) + ' node: ' + str(self.peer))
print 'Closing connection to old protocol version', self.remoteProtocolVersion, 'node: ', self.peer
return return
timestamp, = unpack('>Q', data[12:20]) timestamp, = unpack('>Q', data[12:20])
timeOffset = timestamp - int(time.time()) timeOffset = timestamp - int(time.time())
@ -698,19 +706,28 @@ class receiveDataThread(threading.Thread):
data[80:84]) data[80:84])
readPosition = 80 + lengthOfUseragentVarint readPosition = 80 + lengthOfUseragentVarint
useragent = data[readPosition:readPosition + useragentLength] useragent = data[readPosition:readPosition + useragentLength]
# version check
userAgentName, userAgentVersion = useragent[1:-1].split(":")
if userAgentName == "PyBitmessage":
myVersion = [int(n) for n in shared.softwareVersion.split(".")]
remoteVersion = [int(n) for n in userAgentVersion.split(".")]
# remote is newer, but do not cross between stable and unstable
if cmp(remoteVersion, myVersion) > 0 and \
(myVersion[1] % 2 == remoteVersion[1] % 2):
shared.UISignalQueue.put(('newVersionAvailable', remoteVersion))
readPosition += useragentLength readPosition += useragentLength
numberOfStreamsInVersionMessage, lengthOfNumberOfStreamsInVersionMessage = decodeVarint( numberOfStreamsInVersionMessage, lengthOfNumberOfStreamsInVersionMessage = decodeVarint(
data[readPosition:]) data[readPosition:])
readPosition += lengthOfNumberOfStreamsInVersionMessage readPosition += lengthOfNumberOfStreamsInVersionMessage
self.streamNumber, lengthOfRemoteStreamNumber = decodeVarint( self.streamNumber, lengthOfRemoteStreamNumber = decodeVarint(
data[readPosition:]) data[readPosition:])
with shared.printLock: logger.debug('Remote node useragent: ' + useragent + ' stream number:' + str(self.streamNumber) + ' time offset: ' + str(timeOffset) + ' seconds.')
print 'Remote node useragent:', useragent, ' stream number:', self.streamNumber, ' time offset:', timeOffset, 'seconds.'
if self.streamNumber != 1: if self.streamNumber != 1:
self.sendDataThreadQueue.put((0, 'shutdown','no data')) self.sendDataThreadQueue.put((0, 'shutdown','no data'))
with shared.printLock: logger.debug ('Closed connection to ' + str(self.peer) + ' because they are interested in stream ' + str(self.streamNumber) + '.')
print 'Closed connection to', self.peer, 'because they are interested in stream', self.streamNumber, '.'
return return
shared.connectedHostsList[ shared.connectedHostsList[
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. 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.
@ -720,8 +737,7 @@ class receiveDataThread(threading.Thread):
self.sendDataThreadQueue.put((0, 'setStreamNumber', self.streamNumber)) self.sendDataThreadQueue.put((0, 'setStreamNumber', self.streamNumber))
if data[72:80] == shared.eightBytesOfRandomDataUsedToDetectConnectionsToSelf: if data[72:80] == shared.eightBytesOfRandomDataUsedToDetectConnectionsToSelf:
self.sendDataThreadQueue.put((0, 'shutdown','no data')) self.sendDataThreadQueue.put((0, 'shutdown','no data'))
with shared.printLock: logger.debug('Closing connection to myself: ' + str(self.peer))
print 'Closing connection to myself: ', self.peer
return return
# The other peer's protocol version is of interest to the sendDataThread but we learn of it # The other peer's protocol version is of interest to the sendDataThread but we learn of it
@ -738,16 +754,14 @@ class receiveDataThread(threading.Thread):
# Sends a version message # Sends a version message
def sendversion(self): def sendversion(self):
with shared.printLock: logger.debug('Sending version message')
print 'Sending version message'
self.sendDataThreadQueue.put((0, 'sendRawData', shared.assembleVersionMessage( self.sendDataThreadQueue.put((0, 'sendRawData', shared.assembleVersionMessage(
self.peer.host, self.peer.port, self.streamNumber))) self.peer.host, self.peer.port, self.streamNumber, not self.initiatedConnection)))
# Sends a verack message # Sends a verack message
def sendverack(self): def sendverack(self):
with shared.printLock: logger.debug('Sending verack')
print 'Sending verack'
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('verack'))) self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('verack')))
self.verackSent = True self.verackSent = True
if self.verackReceived: if self.verackReceived:

View File

@ -11,13 +11,14 @@ import socket
from helper_generic import addDataPadding from helper_generic import addDataPadding
from class_objectHashHolder import * from class_objectHashHolder import *
from addresses import * from addresses import *
from debug import logger
# Every connection to a peer has a sendDataThread (and also a # Every connection to a peer has a sendDataThread (and also a
# receiveDataThread). # receiveDataThread).
class sendDataThread(threading.Thread): class sendDataThread(threading.Thread):
def __init__(self, sendDataThreadQueue): def __init__(self, sendDataThreadQueue):
threading.Thread.__init__(self) threading.Thread.__init__(self, name="sendData")
self.sendDataThreadQueue = sendDataThreadQueue self.sendDataThreadQueue = sendDataThreadQueue
shared.sendDataQueues.append(self.sendDataThreadQueue) shared.sendDataQueues.append(self.sendDataThreadQueue)
self.data = '' self.data = ''
@ -35,29 +36,33 @@ class sendDataThread(threading.Thread):
someObjectsOfWhichThisRemoteNodeIsAlreadyAware): someObjectsOfWhichThisRemoteNodeIsAlreadyAware):
self.sock = sock self.sock = sock
self.peer = shared.Peer(HOST, PORT) self.peer = shared.Peer(HOST, PORT)
self.name = "sendData-" + self.peer.host
self.streamNumber = streamNumber self.streamNumber = streamNumber
self.services = 0
self.initiatedConnection = False
self.remoteProtocolVersion = - \ self.remoteProtocolVersion = - \
1 # This must be set using setRemoteProtocolVersion command which is sent through the self.sendDataThreadQueue queue. 1 # This must be set using setRemoteProtocolVersion command which is sent through the self.sendDataThreadQueue queue.
self.lastTimeISentData = int( self.lastTimeISentData = int(
time.time()) # If this value increases beyond five minutes ago, we'll send a pong message to keep the connection alive. time.time()) # If this value increases beyond five minutes ago, we'll send a pong message to keep the connection alive.
self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware
with shared.printLock: if self.streamNumber == -1: # This was an incoming connection.
print 'The streamNumber of this sendDataThread (ID:', str(id(self)) + ') at setup() is', self.streamNumber self.initiatedConnection = False
else:
self.initiatedConnection = True
logger.debug('The streamNumber of this sendDataThread (ID: ' + str(id(self)) + ') at setup() is' + str(self.streamNumber))
def sendVersionMessage(self): def sendVersionMessage(self):
datatosend = shared.assembleVersionMessage( datatosend = shared.assembleVersionMessage(
self.peer.host, self.peer.port, self.streamNumber) # the IP and port of the remote host, and my streamNumber. self.peer.host, self.peer.port, self.streamNumber, not self.initiatedConnection) # the IP and port of the remote host, and my streamNumber.
with shared.printLock: logger.debug('Sending version packet: ' + repr(datatosend))
print 'Sending version packet: ', repr(datatosend)
try: try:
self.sendBytes(datatosend) self.sendBytes(datatosend)
except Exception as err: except Exception as err:
# if not 'Bad file descriptor' in err: # if not 'Bad file descriptor' in err:
with shared.printLock: logger.error('sock.sendall error: %s\n' % err)
sys.stderr.write('sock.sendall error: %s\n' % err)
self.versionSent = 1 self.versionSent = 1
@ -82,7 +87,12 @@ class sendDataThread(threading.Thread):
uploadRateLimitBytes = 999999999 # float("inf") doesn't work uploadRateLimitBytes = 999999999 # float("inf") doesn't work
else: else:
uploadRateLimitBytes = shared.config.getint('bitmessagesettings', 'maxuploadrate') * 1000 uploadRateLimitBytes = shared.config.getint('bitmessagesettings', 'maxuploadrate') * 1000
amountSent = self.sock.send(data[:1000]) if ((self.services & shared.NODE_SSL == shared.NODE_SSL) and
self.connectionIsOrWasFullyEstablished and
shared.haveSSL(not self.initiatedConnection)):
amountSent = self.sslSock.send(data[:1000])
else:
amountSent = self.sock.send(data[:1000])
shared.numberOfBytesSent += amountSent # used for the 'network status' tab in the UI shared.numberOfBytesSent += amountSent # used for the 'network status' tab in the UI
shared.numberOfBytesSentLastSecond += amountSent shared.numberOfBytesSentLastSecond += amountSent
self.lastTimeISentData = int(time.time()) self.lastTimeISentData = int(time.time())
@ -90,15 +100,13 @@ class sendDataThread(threading.Thread):
def run(self): def run(self):
with shared.printLock: logger.debug('sendDataThread starting. ID: ' + str(id(self)) + '. Number of queues in sendDataQueues: ' + str(len(shared.sendDataQueues)))
print 'sendDataThread starting. ID:', str(id(self))+'. Number of queues in sendDataQueues:', len(shared.sendDataQueues)
while True: while True:
deststream, command, data = self.sendDataThreadQueue.get() deststream, command, data = self.sendDataThreadQueue.get()
if deststream == self.streamNumber or deststream == 0: if deststream == self.streamNumber or deststream == 0:
if command == 'shutdown': if command == 'shutdown':
with shared.printLock: logger.debug('sendDataThread (associated with ' + str(self.peer) + ') ID: ' + str(id(self)) + ' shutting down now.')
print 'sendDataThread (associated with', self.peer, ') ID:', id(self), 'shutting down now.'
break break
# When you receive an incoming connection, a sendDataThread is # When you receive an incoming connection, a sendDataThread is
# created even though you don't yet know what stream number the # created even though you don't yet know what stream number the
@ -108,12 +116,10 @@ class sendDataThread(threading.Thread):
# streamNumber of this send data thread here: # streamNumber of this send data thread here:
elif command == 'setStreamNumber': elif command == 'setStreamNumber':
self.streamNumber = data self.streamNumber = data
with shared.printLock: logger.debug('setting the stream number in the sendData thread (ID: ' + str(id(self)) + ') to ' + str(self.streamNumber))
print 'setting the stream number in the sendData thread (ID:', id(self), ') to', self.streamNumber
elif command == 'setRemoteProtocolVersion': elif command == 'setRemoteProtocolVersion':
specifiedRemoteProtocolVersion = data specifiedRemoteProtocolVersion = data
with shared.printLock: logger.debug('setting the remote node\'s protocol version in the sendDataThread (ID: ' + str(id(self)) + ') to ' + str(specifiedRemoteProtocolVersion))
print 'setting the remote node\'s protocol version in the sendDataThread (ID:', id(self), ') to', specifiedRemoteProtocolVersion
self.remoteProtocolVersion = specifiedRemoteProtocolVersion self.remoteProtocolVersion = specifiedRemoteProtocolVersion
elif command == 'advertisepeer': elif command == 'advertisepeer':
self.objectHashHolderInstance.holdPeer(data) self.objectHashHolderInstance.holdPeer(data)
@ -136,8 +142,7 @@ class sendDataThread(threading.Thread):
try: try:
self.sendBytes(packet) self.sendBytes(packet)
except: except:
with shared.printLock: logger.error('sendaddr: self.sock.sendall failed')
print 'sendaddr: self.sock.sendall failed'
break break
elif command == 'advertiseobject': elif command == 'advertiseobject':
self.objectHashHolderInstance.holdHash(data) self.objectHashHolderInstance.holdHash(data)
@ -153,34 +158,30 @@ class sendDataThread(threading.Thread):
try: try:
self.sendBytes(packet) self.sendBytes(packet)
except: except:
with shared.printLock: logger.error('sendinv: self.sock.sendall failed')
print 'sendinv: self.sock.sendall failed'
break break
elif command == 'pong': 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. 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): if self.lastTimeISentData < (int(time.time()) - 298):
# Send out a pong message to keep the connection alive. # Send out a pong message to keep the connection alive.
with shared.printLock: logger.debug('Sending pong to ' + str(self.peer) + ' to keep connection alive.')
print 'Sending pong to', self.peer, 'to keep connection alive.'
packet = shared.CreatePacket('pong') packet = shared.CreatePacket('pong')
try: try:
self.sendBytes(packet) self.sendBytes(packet)
except: except:
with shared.printLock: logger.error('send pong failed')
print 'send pong failed'
break break
elif command == 'sendRawData': elif command == 'sendRawData':
try: try:
self.sendBytes(data) self.sendBytes(data)
except: except:
with shared.printLock: logger.error('Sending of data to ' + str(self.peer) + ' failed. sendDataThread thread ' + str(self) + ' ending now.')
print 'Sending of data to', self.peer, 'failed. sendDataThread thread', self, 'ending now.'
break break
elif command == 'connectionIsOrWasFullyEstablished': elif command == 'connectionIsOrWasFullyEstablished':
self.connectionIsOrWasFullyEstablished = True self.connectionIsOrWasFullyEstablished = True
self.services, self.sslSock = data
else: else:
with shared.printLock: logger.error('sendDataThread ID: ' + str(id(self)) + ' ignoring command ' + command + ' because the thread is not in stream' + str(deststream))
print 'sendDataThread ID:', id(self), 'ignoring command', command, 'because the thread is not in stream', deststream
try: try:
self.sock.shutdown(socket.SHUT_RDWR) self.sock.shutdown(socket.SHUT_RDWR)
@ -188,6 +189,5 @@ class sendDataThread(threading.Thread):
except: except:
pass pass
shared.sendDataQueues.remove(self.sendDataThreadQueue) shared.sendDataQueues.remove(self.sendDataThreadQueue)
with shared.printLock: logger.info('sendDataThread ending. ID: ' + str(id(self)) + '. Number of queues in sendDataQueues: ' + str(len(shared.sendDataQueues)))
print 'sendDataThread ending. ID:', str(id(self))+'. Number of queues in sendDataQueues:', len(shared.sendDataQueues)
self.objectHashHolderInstance.close() self.objectHashHolderInstance.close()

View File

@ -7,6 +7,7 @@ import pickle
import tr#anslate import tr#anslate
from helper_sql import * from helper_sql import *
from helper_threading import *
from debug import logger from debug import logger
""" """
@ -28,10 +29,11 @@ resends msg messages in 5 days (then 10 days, then 20 days, etc...)
""" """
class singleCleaner(threading.Thread): class singleCleaner(threading.Thread, StoppableThread):
def __init__(self): def __init__(self):
threading.Thread.__init__(self) threading.Thread.__init__(self, name="singleCleaner")
self.initStop()
def run(self): def run(self):
timeWeLastClearedInventoryAndPubkeysTables = 0 timeWeLastClearedInventoryAndPubkeysTables = 0
@ -41,7 +43,7 @@ class singleCleaner(threading.Thread):
# Either the user hasn't set stopresendingafterxdays and stopresendingafterxmonths yet or the options are missing from the config file. # Either the user hasn't set stopresendingafterxdays and stopresendingafterxmonths yet or the options are missing from the config file.
shared.maximumLengthOfTimeToBotherResendingMessages = float('inf') shared.maximumLengthOfTimeToBotherResendingMessages = float('inf')
while True: while shared.shutdown == 0:
shared.UISignalQueue.put(( shared.UISignalQueue.put((
'updateStatusBar', 'Doing housekeeping (Flushing inventory in memory to disk...)')) 'updateStatusBar', 'Doing housekeeping (Flushing inventory in memory to disk...)'))
with shared.inventoryLock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. with shared.inventoryLock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock.
@ -83,10 +85,8 @@ class singleCleaner(threading.Thread):
int(time.time()) - shared.maximumLengthOfTimeToBotherResendingMessages) int(time.time()) - shared.maximumLengthOfTimeToBotherResendingMessages)
for row in queryreturn: for row in queryreturn:
if len(row) < 2: if len(row) < 2:
with shared.printLock: logger.error('Something went wrong in the singleCleaner thread: a query did not return the requested fields. ' + repr(row))
sys.stderr.write( self.stop.wait(3)
'Something went wrong in the singleCleaner thread: a query did not return the requested fields. ' + repr(row))
time.sleep(3)
break break
toAddress, ackData, status = row toAddress, ackData, status = row
if status == 'awaitingpubkey': if status == 'awaitingpubkey':
@ -123,7 +123,7 @@ class singleCleaner(threading.Thread):
os._exit(0) os._exit(0)
shared.knownNodesLock.release() shared.knownNodesLock.release()
shared.needToWriteKnownNodesToDisk = False shared.needToWriteKnownNodesToDisk = False
time.sleep(300) self.stop.wait(300)
def resendPubkeyRequest(address): def resendPubkeyRequest(address):

View File

@ -4,6 +4,7 @@ import socket
from class_sendDataThread import * from class_sendDataThread import *
from class_receiveDataThread import * from class_receiveDataThread import *
import helper_bootstrap import helper_bootstrap
from helper_threading import *
import errno import errno
import re import re
@ -15,10 +16,11 @@ import re
# (within the recversion function of the recieveData thread) # (within the recversion function of the recieveData thread)
class singleListener(threading.Thread): class singleListener(threading.Thread, StoppableThread):
def __init__(self): def __init__(self):
threading.Thread.__init__(self) threading.Thread.__init__(self, name="singleListener")
self.initStop()
def setup(self, selfInitiatedConnections): def setup(self, selfInitiatedConnections):
self.selfInitiatedConnections = selfInitiatedConnections self.selfInitiatedConnections = selfInitiatedConnections
@ -37,6 +39,16 @@ class singleListener(threading.Thread):
sock.bind((HOST, PORT)) sock.bind((HOST, PORT))
sock.listen(2) sock.listen(2)
return sock return sock
def stopThread(self):
super(singleListener, self).stopThread()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect(('127.0.0.1', shared.config.getint('bitmessagesettings', 'port')))
s.shutdown(socket.SHUT_RDWR)
s.close()
except:
pass
def run(self): def run(self):
# If there is a trusted peer then we don't want to accept # If there is a trusted peer then we don't want to accept
@ -44,18 +56,17 @@ class singleListener(threading.Thread):
if shared.trustedPeer: if shared.trustedPeer:
return return
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect') and shared.shutdown == 0:
time.sleep(1) self.stop.wait(1)
helper_bootstrap.dns() helper_bootstrap.dns()
# We typically don't want to accept incoming connections if the user is using a # We typically don't want to accept incoming connections if the user is using a
# SOCKS proxy, unless they have configured otherwise. If they eventually select # SOCKS proxy, unless they have configured otherwise. If they eventually select
# proxy 'none' or configure SOCKS listening then this will start listening for # proxy 'none' or configure SOCKS listening then this will start listening for
# connections. # connections.
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten'): while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0:
time.sleep(5) self.stop.wait(5)
with shared.printLock: logger.info('Listening for incoming connections.')
print 'Listening for incoming connections.'
# First try listening on an IPv6 socket. This should also be # First try listening on an IPv6 socket. This should also be
# able to accept connections on IPv4. If that's not available # able to accept connections on IPv4. If that's not available
@ -74,20 +85,19 @@ class singleListener(threading.Thread):
# regexp to match an IPv4-mapped IPv6 address # regexp to match an IPv4-mapped IPv6 address
mappedAddressRegexp = re.compile(r'^::ffff:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$') mappedAddressRegexp = re.compile(r'^::ffff:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$')
while True: while shared.shutdown == 0:
# We typically don't want to accept incoming connections if the user is using a # We typically don't want to accept incoming connections if the user is using a
# SOCKS proxy, unless they have configured otherwise. If they eventually select # SOCKS proxy, unless they have configured otherwise. If they eventually select
# proxy 'none' or configure SOCKS listening then this will start listening for # proxy 'none' or configure SOCKS listening then this will start listening for
# connections. # connections.
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten'): while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0:
time.sleep(10) self.stop.wait(10)
while len(shared.connectedHostsList) > 220: while len(shared.connectedHostsList) > 220 and shared.shutdown == 0:
with shared.printLock: logger.info('We are connected to too many people. Not accepting further incoming connections for ten seconds.')
print 'We are connected to too many people. Not accepting further incoming connections for ten seconds.'
time.sleep(10) self.stop.wait(10)
while True: while shared.shutdown == 0:
socketObject, sockaddr = sock.accept() socketObject, sockaddr = sock.accept()
(HOST, PORT) = sockaddr[0:2] (HOST, PORT) = sockaddr[0:2]
@ -104,8 +114,7 @@ class singleListener(threading.Thread):
# connection flooding. # connection flooding.
if HOST in shared.connectedHostsList: if HOST in shared.connectedHostsList:
socketObject.close() socketObject.close()
with shared.printLock: logger.info('We are already connected to ' + str(HOST) + '. Ignoring connection.')
print 'We are already connected to', HOST + '. Ignoring connection.'
else: else:
break break
@ -124,6 +133,5 @@ class singleListener(threading.Thread):
socketObject, HOST, PORT, -1, someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections, sendDataThreadQueue) socketObject, HOST, PORT, -1, someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections, sendDataThreadQueue)
rd.start() rd.start()
with shared.printLock: logger.info('connected to ' + HOST + ' during INCOMING request.')
print self, 'connected to', HOST, 'during INCOMING request.'

View File

@ -15,17 +15,32 @@ from debug import logger
from helper_sql import * from helper_sql import *
import helper_inbox import helper_inbox
from helper_generic import addDataPadding from helper_generic import addDataPadding
from helper_threading import *
import l10n import l10n
# This thread, of which there is only one, does the heavy lifting: # This thread, of which there is only one, does the heavy lifting:
# calculating POWs. # calculating POWs.
def sizeof_fmt(num, suffix='h/s'):
for unit in ['','k','M','G','T','P','E','Z']:
if abs(num) < 1000.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
class singleWorker(threading.Thread): class singleWorker(threading.Thread, StoppableThread):
def __init__(self): def __init__(self):
# QThread.__init__(self, parent) # QThread.__init__(self, parent)
threading.Thread.__init__(self) threading.Thread.__init__(self, name="singleWorker")
self.initStop()
def stopThread(self):
try:
shared.workerQueue.put(("stopThread", "data"))
except:
pass
super(singleWorker, self).stopThread()
def run(self): def run(self):
@ -49,10 +64,10 @@ class singleWorker(threading.Thread):
'''SELECT ackdata FROM sent where (status='msgsent' OR status='doingmsgpow')''') '''SELECT ackdata FROM sent where (status='msgsent' OR status='doingmsgpow')''')
for row in queryreturn: for row in queryreturn:
ackdata, = row ackdata, = row
print 'Watching for ackdata', ackdata.encode('hex') logger.info('Watching for ackdata ' + ackdata.encode('hex'))
shared.ackdataForWhichImWatching[ackdata] = 0 shared.ackdataForWhichImWatching[ackdata] = 0
time.sleep( self.stop.wait(
10) # give some time for the GUI to start before we start on existing POW tasks. 10) # give some time for the GUI to start before we start on existing POW tasks.
queryreturn = sqlQuery( queryreturn = sqlQuery(
@ -68,7 +83,7 @@ class singleWorker(threading.Thread):
# just in case there are any tasks for Broadcasts # just in case there are any tasks for Broadcasts
# that have yet to be sent. # that have yet to be sent.
while True: while shared.shutdown == 0:
command, data = shared.workerQueue.get() command, data = shared.workerQueue.get()
if command == 'sendmessage': if command == 'sendmessage':
self.sendMsg() self.sendMsg()
@ -80,10 +95,10 @@ class singleWorker(threading.Thread):
self.sendOutOrStoreMyV3Pubkey(data) self.sendOutOrStoreMyV3Pubkey(data)
elif command == 'sendOutOrStoreMyV4Pubkey': elif command == 'sendOutOrStoreMyV4Pubkey':
self.sendOutOrStoreMyV4Pubkey(data) self.sendOutOrStoreMyV4Pubkey(data)
elif command == 'stopThread':
return
else: else:
with shared.printLock: logger.error('Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command)
sys.stderr.write(
'Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command)
shared.workerQueue.task_done() shared.workerQueue.task_done()
@ -114,9 +129,7 @@ class singleWorker(threading.Thread):
privEncryptionKeyBase58 = shared.config.get( privEncryptionKeyBase58 = shared.config.get(
myAddress, 'privencryptionkey') myAddress, 'privencryptionkey')
except Exception as err: except Exception as err:
with shared.printLock: logger.error('Error within doPOWForMyV2Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
sys.stderr.write(
'Error within doPOWForMyV2Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
return return
privSigningKeyHex = shared.decodeWalletImportFormat( privSigningKeyHex = shared.decodeWalletImportFormat(
@ -133,10 +146,10 @@ class singleWorker(threading.Thread):
# Do the POW for this pubkey message # Do the POW for this pubkey message
target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16))))
print '(For pubkey message) Doing proof of work...' logger.info('(For pubkey message) Doing proof of work...')
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce logger.info('(For pubkey message) Found proof of work ' + str(trialValue), ' Nonce: ', str(nonce))
payload = pack('>Q', nonce) + payload payload = pack('>Q', nonce) + payload
inventoryHash = calculateInventoryHash(payload) inventoryHash = calculateInventoryHash(payload)
@ -145,8 +158,7 @@ class singleWorker(threading.Thread):
objectType, streamNumber, payload, embeddedTime,'') objectType, streamNumber, payload, embeddedTime,'')
shared.inventorySets[streamNumber].add(inventoryHash) shared.inventorySets[streamNumber].add(inventoryHash)
with shared.printLock: logger.info('broadcasting inv with hash: ' + inventoryHash.encode('hex'))
print 'broadcasting inv with hash:', inventoryHash.encode('hex')
shared.broadcastToSendDataQueues(( shared.broadcastToSendDataQueues((
streamNumber, 'advertiseobject', inventoryHash)) streamNumber, 'advertiseobject', inventoryHash))
@ -171,8 +183,7 @@ class singleWorker(threading.Thread):
#The address has been deleted. #The address has been deleted.
return return
if shared.safeConfigGetBoolean(myAddress, 'chan'): if shared.safeConfigGetBoolean(myAddress, 'chan'):
with shared.printLock: logger.info('This is a chan address. Not sending pubkey.')
print 'This is a chan address. Not sending pubkey.'
return return
status, addressVersionNumber, streamNumber, hash = decodeAddress( status, addressVersionNumber, streamNumber, hash = decodeAddress(
myAddress) myAddress)
@ -199,9 +210,7 @@ class singleWorker(threading.Thread):
privEncryptionKeyBase58 = shared.config.get( privEncryptionKeyBase58 = shared.config.get(
myAddress, 'privencryptionkey') myAddress, 'privencryptionkey')
except Exception as err: except Exception as err:
with shared.printLock: logger.error('Error within sendOutOrStoreMyV3Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
sys.stderr.write(
'Error within sendOutOrStoreMyV3Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
return return
@ -228,12 +237,10 @@ class singleWorker(threading.Thread):
# Do the POW for this pubkey message # Do the POW for this pubkey message
target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16))))
with shared.printLock: logger.info('(For pubkey message) Doing proof of work...')
print '(For pubkey message) Doing proof of work...'
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
with shared.printLock: logger.info('(For pubkey message) Found proof of work. Nonce: ' + str(nonce))
print '(For pubkey message) Found proof of work. Nonce:', nonce
payload = pack('>Q', nonce) + payload payload = pack('>Q', nonce) + payload
inventoryHash = calculateInventoryHash(payload) inventoryHash = calculateInventoryHash(payload)
@ -242,8 +249,7 @@ class singleWorker(threading.Thread):
objectType, streamNumber, payload, embeddedTime,'') objectType, streamNumber, payload, embeddedTime,'')
shared.inventorySets[streamNumber].add(inventoryHash) shared.inventorySets[streamNumber].add(inventoryHash)
with shared.printLock: logger.info('broadcasting inv with hash: ' + inventoryHash.encode('hex'))
print 'broadcasting inv with hash:', inventoryHash.encode('hex')
shared.broadcastToSendDataQueues(( shared.broadcastToSendDataQueues((
streamNumber, 'advertiseobject', inventoryHash)) streamNumber, 'advertiseobject', inventoryHash))
@ -264,8 +270,7 @@ class singleWorker(threading.Thread):
#The address has been deleted. #The address has been deleted.
return return
if shared.safeConfigGetBoolean(myAddress, 'chan'): if shared.safeConfigGetBoolean(myAddress, 'chan'):
with shared.printLock: logger.info('This is a chan address. Not sending pubkey.')
print 'This is a chan address. Not sending pubkey.'
return return
status, addressVersionNumber, streamNumber, hash = decodeAddress( status, addressVersionNumber, streamNumber, hash = decodeAddress(
myAddress) myAddress)
@ -285,9 +290,7 @@ class singleWorker(threading.Thread):
privEncryptionKeyBase58 = shared.config.get( privEncryptionKeyBase58 = shared.config.get(
myAddress, 'privencryptionkey') myAddress, 'privencryptionkey')
except Exception as err: except Exception as err:
with shared.printLock: logger.error('Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
sys.stderr.write(
'Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
return return
privSigningKeyHex = shared.decodeWalletImportFormat( privSigningKeyHex = shared.decodeWalletImportFormat(
@ -326,10 +329,10 @@ class singleWorker(threading.Thread):
# Do the POW for this pubkey message # Do the POW for this pubkey message
target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16))))
print '(For pubkey message) Doing proof of work...' logger.info('(For pubkey message) Doing proof of work...')
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce logger.info('(For pubkey message) Found proof of work ' + str(trialValue) + 'Nonce: ' + str(nonce))
payload = pack('>Q', nonce) + payload payload = pack('>Q', nonce) + payload
inventoryHash = calculateInventoryHash(payload) inventoryHash = calculateInventoryHash(payload)
@ -338,8 +341,7 @@ class singleWorker(threading.Thread):
objectType, streamNumber, payload, embeddedTime, doubleHashOfAddressData[32:]) objectType, streamNumber, payload, embeddedTime, doubleHashOfAddressData[32:])
shared.inventorySets[streamNumber].add(inventoryHash) shared.inventorySets[streamNumber].add(inventoryHash)
with shared.printLock: logger.info('broadcasting inv with hash: ' + inventoryHash.encode('hex'))
print 'broadcasting inv with hash:', inventoryHash.encode('hex')
shared.broadcastToSendDataQueues(( shared.broadcastToSendDataQueues((
streamNumber, 'advertiseobject', inventoryHash)) streamNumber, 'advertiseobject', inventoryHash))
@ -359,9 +361,7 @@ class singleWorker(threading.Thread):
status, addressVersionNumber, streamNumber, ripe = decodeAddress( status, addressVersionNumber, streamNumber, ripe = decodeAddress(
fromaddress) fromaddress)
if addressVersionNumber <= 1: if addressVersionNumber <= 1:
with shared.printLock: logger.error('Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n')
sys.stderr.write(
'Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n')
return return
# We need to convert our private keys to public keys in order # We need to convert our private keys to public keys in order
# to include them. # to include them.
@ -442,12 +442,12 @@ class singleWorker(threading.Thread):
dataToEncrypt, pubEncryptionKey.encode('hex')) dataToEncrypt, pubEncryptionKey.encode('hex'))
target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16))))
print '(For broadcast message) Doing proof of work...' logger.info('(For broadcast message) Doing proof of work...')
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast...")))) ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast..."))))
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
print '(For broadcast message) Found proof of work', trialValue, 'Nonce:', nonce logger.info('(For broadcast message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce))
payload = pack('>Q', nonce) + payload payload = pack('>Q', nonce) + payload
@ -463,8 +463,7 @@ class singleWorker(threading.Thread):
shared.inventory[inventoryHash] = ( shared.inventory[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, tag) objectType, streamNumber, payload, embeddedTime, tag)
shared.inventorySets[streamNumber].add(inventoryHash) shared.inventorySets[streamNumber].add(inventoryHash)
with shared.printLock: logger.info('sending inv (within sendBroadcast function) for object: ' + inventoryHash.encode('hex'))
print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex')
shared.broadcastToSendDataQueues(( shared.broadcastToSendDataQueues((
streamNumber, 'advertiseobject', inventoryHash)) streamNumber, 'advertiseobject', inventoryHash))
@ -612,8 +611,8 @@ class singleWorker(threading.Thread):
shared.ackdataForWhichImWatching[ackdata] = 0 shared.ackdataForWhichImWatching[ackdata] = 0
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key")))) ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key"))))
with shared.printLock: logger.info('Sending a message.')
print 'Sending a message. First 150 characters of message:', repr(message[:150]) logger.debug('First 150 characters of message: ' + repr(message[:150]))
# Let us fetch the recipient's public key out of our database. If # Let us fetch the recipient's public key out of our database. If
# the required proof of work difficulty is too hard then we'll # the required proof of work difficulty is too hard then we'll
@ -685,8 +684,8 @@ class singleWorker(threading.Thread):
requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(l10n.formatTimestamp())))) requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(l10n.formatTimestamp()))))
continue continue
else: # if we are sending a message to ourselves or a chan.. else: # if we are sending a message to ourselves or a chan..
with shared.printLock: logger.info('Sending a message.')
print 'Sending a message. First 150 characters of message:', repr(message[:150]) logger.debug('First 150 characters of message: ' + repr(message[:150]))
behaviorBitfield = '\x00\x00\x00\x01' behaviorBitfield = '\x00\x00\x00\x01'
try: try:
@ -694,9 +693,7 @@ class singleWorker(threading.Thread):
toaddress, 'privencryptionkey') toaddress, 'privencryptionkey')
except Exception as err: except Exception as err:
shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1').arg(l10n.formatTimestamp())))) shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1').arg(l10n.formatTimestamp()))))
with shared.printLock: logger.error('Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s\n' % err)
sys.stderr.write(
'Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s\n' % err)
continue continue
privEncryptionKeyHex = shared.decodeWalletImportFormat( privEncryptionKeyHex = shared.decodeWalletImportFormat(
privEncryptionKeyBase58).encode('hex') privEncryptionKeyBase58).encode('hex')
@ -761,12 +758,10 @@ class singleWorker(threading.Thread):
payload += encodeVarint(len(messageToTransmit)) payload += encodeVarint(len(messageToTransmit))
payload += messageToTransmit payload += messageToTransmit
if shared.config.has_section(toaddress): if shared.config.has_section(toaddress):
with shared.printLock: logger.info('Not bothering to include ackdata because we are sending to ourselves or a chan.')
print 'Not bothering to include ackdata because we are sending to ourselves or a chan.'
fullAckPayload = '' fullAckPayload = ''
elif not shared.isBitSetWithinBitfield(behaviorBitfield,31): elif not shared.isBitSetWithinBitfield(behaviorBitfield,31):
with shared.printLock: logger.info('Not bothering to include ackdata because the receiver said that they won\'t relay it anyway.')
print 'Not bothering to include ackdata because the receiver said that they won\'t relay it anyway.'
fullAckPayload = '' fullAckPayload = ''
else: else:
fullAckPayload = self.generateFullAckMessage( fullAckPayload = self.generateFullAckMessage(
@ -791,18 +786,16 @@ class singleWorker(threading.Thread):
encryptedPayload += encodeVarint(1) # msg version encryptedPayload += encodeVarint(1) # msg version
encryptedPayload += encodeVarint(toStreamNumber) + encrypted encryptedPayload += encodeVarint(toStreamNumber) + encrypted
target = 2 ** 64 / (requiredAverageProofOfWorkNonceTrialsPerByte*(len(encryptedPayload) + 8 + requiredPayloadLengthExtraBytes + ((TTL*(len(encryptedPayload)+8+requiredPayloadLengthExtraBytes))/(2 ** 16)))) target = 2 ** 64 / (requiredAverageProofOfWorkNonceTrialsPerByte*(len(encryptedPayload) + 8 + requiredPayloadLengthExtraBytes + ((TTL*(len(encryptedPayload)+8+requiredPayloadLengthExtraBytes))/(2 ** 16))))
with shared.printLock: logger.info('(For msg message) Doing proof of work. Total required difficulty: %f. Required small message difficulty: %f.', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)
print '(For msg message) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes
powStartTime = time.time() powStartTime = time.time()
initialHash = hashlib.sha512(encryptedPayload).digest() initialHash = hashlib.sha512(encryptedPayload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
with shared.printLock: logger.info('(For msg message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce))
print '(For msg message) Found proof of work', trialValue, 'Nonce:', nonce try:
try: logger.info('PoW took %.1f seconds, speed %s.', time.time() - powStartTime, sizeof_fmt(nonce / (time.time() - powStartTime)))
print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.' except:
except: pass
pass
encryptedPayload = pack('>Q', nonce) + encryptedPayload encryptedPayload = pack('>Q', nonce) + encryptedPayload
@ -823,7 +816,7 @@ class singleWorker(threading.Thread):
else: else:
# not sending to a chan or one of my addresses # not sending to a chan or one of my addresses
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting for acknowledgement. Sent on %1").arg(l10n.formatTimestamp())))) shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting for acknowledgement. Sent on %1").arg(l10n.formatTimestamp()))))
print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex') logger.info('Broadcasting inv for my msg(within sendmsg function):' + inventoryHash.encode('hex'))
shared.broadcastToSendDataQueues(( shared.broadcastToSendDataQueues((
toStreamNumber, 'advertiseobject', inventoryHash)) toStreamNumber, 'advertiseobject', inventoryHash))
@ -871,8 +864,7 @@ class singleWorker(threading.Thread):
toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress( toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress(
toAddress) toAddress)
if toStatus != 'success': if toStatus != 'success':
with shared.printLock: logger.error('Very abnormal error occurred in requestPubKey. toAddress is: ' + repr(
sys.stderr.write('Very abnormal error occurred in requestPubKey. toAddress is: ' + repr(
toAddress) + '. Please report this error to Atheros.') toAddress) + '. Please report this error to Atheros.')
return return
@ -907,12 +899,10 @@ class singleWorker(threading.Thread):
payload += encodeVarint(streamNumber) payload += encodeVarint(streamNumber)
if addressVersionNumber <= 3: if addressVersionNumber <= 3:
payload += ripe payload += ripe
with shared.printLock: logger.info('making request for pubkey with ripe:', ripe.encode('hex'))
print 'making request for pubkey with ripe:', ripe.encode('hex')
else: else:
payload += tag payload += tag
with shared.printLock: logger.info('making request for v4 pubkey with tag:', tag.encode('hex'))
print 'making request for v4 pubkey with tag:', tag.encode('hex')
# print 'trial value', trialValue # print 'trial value', trialValue
statusbar = 'Doing the computations necessary to request the recipient\'s public key.' statusbar = 'Doing the computations necessary to request the recipient\'s public key.'
@ -923,8 +913,7 @@ class singleWorker(threading.Thread):
target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16))))
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
with shared.printLock: logger.info('Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce))
print 'Found proof of work', trialValue, 'Nonce:', nonce
payload = pack('>Q', nonce) + payload payload = pack('>Q', nonce) + payload
inventoryHash = calculateInventoryHash(payload) inventoryHash = calculateInventoryHash(payload)
@ -932,7 +921,7 @@ class singleWorker(threading.Thread):
shared.inventory[inventoryHash] = ( shared.inventory[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '') objectType, streamNumber, payload, embeddedTime, '')
shared.inventorySets[streamNumber].add(inventoryHash) shared.inventorySets[streamNumber].add(inventoryHash)
print 'sending inv (for the getpubkey message)' logger.info('sending inv (for the getpubkey message)')
shared.broadcastToSendDataQueues(( shared.broadcastToSendDataQueues((
streamNumber, 'advertiseobject', inventoryHash)) streamNumber, 'advertiseobject', inventoryHash))
@ -948,7 +937,7 @@ class singleWorker(threading.Thread):
toAddress) toAddress)
shared.UISignalQueue.put(( shared.UISignalQueue.put((
'updateStatusBar', tr.translateText("MainWindow",'Broacasting the public key request. This program will auto-retry if they are offline.'))) 'updateStatusBar', tr.translateText("MainWindow",'Broadcasting the public key request. This program will auto-retry if they are offline.')))
shared.UISignalQueue.put(('updateSentItemStatusByToAddress', (toAddress, tr.translateText("MainWindow",'Sending public key request. Waiting for reply. Requested at %1').arg(l10n.formatTimestamp())))) shared.UISignalQueue.put(('updateSentItemStatusByToAddress', (toAddress, tr.translateText("MainWindow",'Sending public key request. Waiting for reply. Requested at %1').arg(l10n.formatTimestamp()))))
def generateFullAckMessage(self, ackdata, toStreamNumber, TTL): def generateFullAckMessage(self, ackdata, toStreamNumber, TTL):
@ -976,18 +965,16 @@ class singleWorker(threading.Thread):
payload += encodeVarint(toStreamNumber) + ackdata payload += encodeVarint(toStreamNumber) + ackdata
target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16)))) target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16))))
with shared.printLock: logger.info('(For ack message) Doing proof of work. TTL set to ' + str(TTL))
print '(For ack message) Doing proof of work. TTL set to', TTL
powStartTime = time.time() powStartTime = time.time()
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
with shared.printLock: logger.info('(For ack message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce))
print '(For ack message) Found proof of work', trialValue, 'Nonce:', nonce try:
try: logger.info('PoW took %.1f seconds, speed %s.', time.time() - powStartTime, sizeof_fmt(nonce / (time.time() - powStartTime)))
print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.' except:
except: pass
pass
payload = pack('>Q', nonce) + payload payload = pack('>Q', nonce) + payload
return shared.CreatePacket('object', payload) return shared.CreatePacket('object', payload)

View File

@ -19,12 +19,14 @@ import tr#anslate
class sqlThread(threading.Thread): class sqlThread(threading.Thread):
def __init__(self): def __init__(self):
threading.Thread.__init__(self) threading.Thread.__init__(self, name="SQL")
def run(self): def run(self):
self.conn = sqlite3.connect(shared.appdata + 'messages.dat') self.conn = sqlite3.connect(shared.appdata + 'messages.dat')
self.conn.text_factory = str self.conn.text_factory = str
self.cur = self.conn.cursor() self.cur = self.conn.cursor()
self.cur.execute('PRAGMA secure_delete = true')
try: try:
self.cur.execute( self.cur.execute(
@ -450,7 +452,7 @@ class sqlThread(threading.Thread):
queryreturn = self.cur.fetchall() queryreturn = self.cur.fetchall()
for row in queryreturn: for row in queryreturn:
value, = row value, = row
if int(value) < int(time.time()) - 2592000: if int(value) < int(time.time()) - 86400:
logger.info('It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...') logger.info('It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...')
try: try:
self.cur.execute( ''' VACUUM ''') self.cur.execute( ''' VACUUM ''')

View File

@ -18,15 +18,33 @@ Use: `from debug import logger` to import this facility into whatever module you
''' '''
import logging import logging
import logging.config import logging.config
import os
import shared import shared
import sys import sys
import traceback
import helper_startup import helper_startup
helper_startup.loadConfig() helper_startup.loadConfig()
# TODO(xj9): Get from a config file. # TODO(xj9): Get from a config file.
log_level = 'DEBUG' log_level = 'DEBUG'
def log_uncaught_exceptions(ex_cls, ex, tb):
logging.critical(''.join(traceback.format_tb(tb)))
logging.critical('{0}: {1}'.format(ex_cls, ex))
def configureLogging(): def configureLogging():
have_logging = False
try:
logging.config.fileConfig(os.path.join (shared.appdata, 'logging.dat'))
have_logging = True
except:
pass
sys.excepthook = log_uncaught_exceptions
if have_logging:
return False
logging.config.dictConfig({ logging.config.dictConfig({
'version': 1, 'version': 1,
'formatters': { 'formatters': {
@ -69,13 +87,17 @@ def configureLogging():
'handlers': ['console'], 'handlers': ['console'],
}, },
}) })
return True
# TODO (xj9): Get from a config file. # TODO (xj9): Get from a config file.
#logger = logging.getLogger('console_only') #logger = logging.getLogger('console_only')
configureLogging() if configureLogging():
if '-c' in sys.argv: if '-c' in sys.argv:
logger = logging.getLogger('file_only') logger = logging.getLogger('file_only')
else:
logger = logging.getLogger('both')
else: else:
logger = logging.getLogger('both') logger = logging.getLogger('default')
def restartLoggingInUpdatedAppdataLocation(): def restartLoggingInUpdatedAppdataLocation():
global logger global logger
@ -83,9 +105,11 @@ def restartLoggingInUpdatedAppdataLocation():
logger.removeHandler(i) logger.removeHandler(i)
i.flush() i.flush()
i.close() i.close()
configureLogging() if configureLogging():
if '-c' in sys.argv: if '-c' in sys.argv:
logger = logging.getLogger('file_only') logger = logging.getLogger('file_only')
else:
logger = logging.getLogger('both')
else: else:
logger = logging.getLogger('both') logger = logging.getLogger('default')

View File

@ -4,6 +4,9 @@ import defaultKnownNodes
import pickle import pickle
import time import time
from debug import logger
import socks
def knownNodes(): def knownNodes():
try: try:
# We shouldn't have to use the shared.knownNodesLock because this had # We shouldn't have to use the shared.knownNodesLock because this had
@ -26,7 +29,7 @@ def knownNodes():
except: except:
shared.knownNodes = defaultKnownNodes.createDefaultKnownNodes(shared.appdata) shared.knownNodes = defaultKnownNodes.createDefaultKnownNodes(shared.appdata)
if shared.config.getint('bitmessagesettings', 'settingsversion') > 10: if shared.config.getint('bitmessagesettings', 'settingsversion') > 10:
print 'Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.' logger.error('Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.')
raise SystemExit raise SystemExit
def dns(): def dns():
@ -35,20 +38,53 @@ def dns():
# defaultKnownNodes.py. Hopefully either they are up to date or the user # defaultKnownNodes.py. Hopefully either they are up to date or the user
# has run Bitmessage recently without SOCKS turned on and received good # has run Bitmessage recently without SOCKS turned on and received good
# bootstrap nodes using that method. # bootstrap nodes using that method.
with shared.printLock: if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none':
if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none': try:
for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80):
logger.info('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method')
shared.knownNodes[1][shared.Peer(item[4][0], 8080)] = int(time.time())
except:
logger.error('bootstrap8080.bitmessage.org DNS bootstrapping failed.')
try:
for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80):
logger.info ('Adding ' + item[4][0] + ' to knownNodes based on DNS bootstrap method')
shared.knownNodes[1][shared.Peer(item[4][0], 8444)] = int(time.time())
except:
logger.error('bootstrap8444.bitmessage.org DNS bootstrapping failed.')
elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
shared.knownNodes[1][shared.Peer('quzwelsuziwqgpt2.onion', 8444)] = int(time.time())
logger.debug("Adding quzwelsuziwqgpt2.onion:8444 to knownNodes.")
for port in [8080, 8444]:
logger.debug("Resolving %i through SOCKS...", port)
address_family = socket.AF_INET
sock = socks.socksocket(address_family, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(20)
proxytype = socks.PROXY_TYPE_SOCKS5
sockshostname = shared.config.get(
'bitmessagesettings', 'sockshostname')
socksport = shared.config.getint(
'bitmessagesettings', 'socksport')
rdns = True # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
if shared.config.getboolean('bitmessagesettings', 'socksauthentication'):
socksusername = shared.config.get(
'bitmessagesettings', 'socksusername')
sockspassword = shared.config.get(
'bitmessagesettings', 'sockspassword')
sock.setproxy(
proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
else:
sock.setproxy(
proxytype, sockshostname, socksport, rdns)
try: try:
for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80): ip = sock.resolve("bootstrap" + str(port) + ".bitmessage.org")
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method' sock.shutdown(socket.SHUT_RDWR)
shared.knownNodes[1][shared.Peer(item[4][0], 8080)] = int(time.time()) sock.close()
except: except:
print 'bootstrap8080.bitmessage.org DNS bootstrapping failed.' logger.error("SOCKS DNS resolving failed", exc_info=True)
try: if ip is not None:
for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80): logger.info ('Adding ' + ip + ' to knownNodes based on SOCKS DNS bootstrap method')
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method' shared.knownNodes[1][shared.Peer(ip, port)] = time.time()
shared.knownNodes[1][shared.Peer(item[4][0], 8444)] = int(time.time()) else:
except: logger.info('DNS bootstrap skipped because the proxy type does not support DNS resolution.')
print 'bootstrap8444.bitmessage.org DNS bootstrapping failed.'
else:
print 'DNS bootstrap skipped because SOCKS is used.'

View File

@ -3,7 +3,8 @@ import shared
def insert(t): def insert(t):
sqlExecute('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?,?)''', *t) sqlExecute('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?,?)''', *t)
shared.UISignalQueue.put(('changedInboxUnread', None)) #shouldn't emit changedInboxUnread and displayNewInboxMessage at the same time
#shared.UISignalQueue.put(('changedInboxUnread', None))
def trash(msgid): def trash(msgid):
sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', msgid) sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', msgid)

View File

@ -11,6 +11,8 @@ def sqlQuery(sqlStatement, *args):
if args == (): if args == ():
sqlSubmitQueue.put('') sqlSubmitQueue.put('')
elif type(args[0]) in [list, tuple]:
sqlSubmitQueue.put(args[0])
else: else:
sqlSubmitQueue.put(args) sqlSubmitQueue.put(args)

View File

@ -145,5 +145,5 @@ def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
return StrictVersion("5.1.2600")<=VER_THIS and StrictVersion("6.0.6000")>=VER_THIS return StrictVersion("5.1.2600")<=VER_THIS and StrictVersion("6.0.6000")>=VER_THIS
return False return False
except Exception as err: except Exception as err:
print "Info: we could not tell whether your OS is limited to having very view half open connections because we couldn't interpret the platform version. Don't worry; we'll assume that it is not limited. This tends to occur on Raspberry Pis. :", err print "Info: we could not tell whether your OS is limited to having very few half open connections because we couldn't interpret the platform version. Don't worry; we'll assume that it is not limited. This tends to occur on Raspberry Pis. :", err
return False return False

10
src/helper_threading.py Normal file
View File

@ -0,0 +1,10 @@
import threading
class StoppableThread(object):
def initStop(self):
self.stop = threading.Event()
self._stopped = False
def stopThread(self):
self._stopped = True
self.stop.set()

93
src/openclpow.py Normal file
View File

@ -0,0 +1,93 @@
#!/usr/bin/env python2.7
from struct import pack, unpack
import time
import hashlib
import random
import os
from shared import codePath, safeConfigGetBoolean
from debug import logger
libAvailable = True
ctx = False
queue = False
program = False
gpus = []
hash_dt = None
try:
import numpy
import pyopencl as cl
except:
libAvailable = False
def initCL():
global ctx, queue, program, gpus, hash_dt
try:
hash_dt = numpy.dtype([('target', numpy.uint64), ('v', numpy.str_, 73)])
for platform in cl.get_platforms():
gpus.extend(platform.get_devices(device_type=cl.device_type.GPU))
if (len(gpus) > 0):
ctx = cl.Context(devices=gpus)
queue = cl.CommandQueue(ctx)
f = open(os.path.join(codePath(), "bitmsghash", 'bitmsghash.cl'), 'r')
fstr = ''.join(f.readlines())
program = cl.Program(ctx, fstr).build(options="")
logger.info("Loaded OpenCL kernel")
else:
logger.info("No OpenCL GPUs found")
ctx = False
except Exception as e:
logger.error("OpenCL fail: ", exc_info=True)
ctx = False
def has_opencl():
global ctx
return (ctx != False)
def do_opencl_pow(hash, target):
global ctx, queue, program, gpus, hash_dt
output = numpy.zeros(1, dtype=[('v', numpy.uint64, 1)])
if (ctx == False):
return output[0][0]
data = numpy.zeros(1, dtype=hash_dt, order='C')
data[0]['v'] = ("0000000000000000" + hash).decode("hex")
data[0]['target'] = target
hash_buf = cl.Buffer(ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=data)
dest_buf = cl.Buffer(ctx, cl.mem_flags.WRITE_ONLY, output.nbytes)
kernel = program.kernel_sha512
worksize = kernel.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, gpus[0])
kernel.set_arg(0, hash_buf)
kernel.set_arg(1, dest_buf)
start = time.time()
progress = 0
globamt = worksize*2000
while output[0][0] == 0:
kernel.set_arg(2, pack("<Q", progress))
cl.enqueue_nd_range_kernel(queue, kernel, (globamt,), (worksize,))
cl.enqueue_read_buffer(queue, dest_buf, output)
queue.finish()
progress += globamt
sofar = time.time() - start
# logger.debug("Working for %.3fs, %.2f Mh/s", sofar, (progress / sofar) / 1000000)
taken = time.time() - start
# logger.debug("Took %d tries.", progress)
return output[0][0]
if libAvailable:
initCL()
if __name__ == "__main__":
target = 54227212183L
initialHash = "3758f55b5a8d902fd3597e4ce6a2d3f23daff735f65d9698c270987f4e67ad590b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3".decode("hex")
nonce = do_opencl_pow(initialHash.encode("hex"), target)
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
print "{} - value {} < {}".format(nonce, trialValue, target)

View File

@ -4,9 +4,44 @@
import hashlib import hashlib
from struct import unpack, pack from struct import unpack, pack
import sys import sys
from shared import config, frozen from debug import logger
import shared from shared import config, frozen, codePath, shutdown, safeConfigGetBoolean, UISignalQueue
#import os import openclpow
import tr
import os
import ctypes
bitmsglib = 'bitmsghash.so'
if "win32" == sys.platform:
if ctypes.sizeof(ctypes.c_voidp) == 4:
bitmsglib = 'bitmsghash32.dll'
else:
bitmsglib = 'bitmsghash64.dll'
try:
# MSVS
bso = ctypes.WinDLL(os.path.join(codePath(), "bitmsghash", bitmsglib))
logger.info("Loaded C PoW DLL (stdcall) %s", bitmsglib)
except:
try:
# MinGW
bso = ctypes.CDLL(os.path.join(codePath(), "bitmsghash", bitmsglib))
logger.info("Loaded C PoW DLL (cdecl) %s", bitmsglib)
except:
bso = None
else:
try:
bso = ctypes.CDLL(os.path.join(codePath(), "bitmsghash", bitmsglib))
logger.info("Loaded C PoW DLL %s", bitmsglib)
except:
bso = None
if bso:
try:
bmpow = bso.BitmessagePOW
bmpow.restype = ctypes.c_ulonglong
except:
bmpow = None
else:
bmpow = None
def _set_idle(): def _set_idle():
if 'linux' in sys.platform: if 'linux' in sys.platform:
@ -32,14 +67,17 @@ def _pool_worker(nonce, initialHash, target, pool_size):
return [trialValue, nonce] return [trialValue, nonce]
def _doSafePoW(target, initialHash): def _doSafePoW(target, initialHash):
logger.debug("Safe PoW start")
nonce = 0 nonce = 0
trialValue = float('inf') trialValue = float('inf')
while trialValue > target: while trialValue > target:
nonce += 1 nonce += 1
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
logger.debug("Safe PoW done")
return [trialValue, nonce] return [trialValue, nonce]
def _doFastPoW(target, initialHash): def _doFastPoW(target, initialHash):
logger.debug("Fast PoW start")
import time import time
from multiprocessing import Pool, cpu_count from multiprocessing import Pool, cpu_count
try: try:
@ -57,7 +95,7 @@ def _doFastPoW(target, initialHash):
for i in range(pool_size): for i in range(pool_size):
result.append(pool.apply_async(_pool_worker, args = (i, initialHash, target, pool_size))) result.append(pool.apply_async(_pool_worker, args = (i, initialHash, target, pool_size)))
while True: while True:
if shared.shutdown >= 1: if shutdown >= 1:
pool.terminate() pool.terminate()
while True: while True:
time.sleep(10) # Don't let this thread return here; it will return nothing and cause an exception in bitmessagemain.py time.sleep(10) # Don't let this thread return here; it will return nothing and cause an exception in bitmessagemain.py
@ -67,12 +105,59 @@ def _doFastPoW(target, initialHash):
result = result[i].get() result = result[i].get()
pool.terminate() pool.terminate()
pool.join() #Wait for the workers to exit... pool.join() #Wait for the workers to exit...
logger.debug("Fast PoW done")
return result[0], result[1] return result[0], result[1]
time.sleep(0.2) time.sleep(0.2)
def _doCPoW(target, initialHash):
h = initialHash
m = target
out_h = ctypes.pointer(ctypes.create_string_buffer(h, 64))
out_m = ctypes.c_ulonglong(m)
logger.debug("C PoW start")
nonce = bmpow(out_h, out_m)
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
logger.debug("C PoW done")
return [trialValue, nonce]
def _doGPUPoW(target, initialHash):
logger.debug("GPU PoW start")
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
#print "{} - value {} < {}".format(nonce, trialValue, target)
if trialValue > target:
deviceNames = ", ".join(gpu.name for gpu in openclpow.gpus)
UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.')))
logger.error("Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.", deviceNames)
openclpow.ctx = False
raise Exception("GPU did not calculate correctly.")
logger.debug("GPU PoW done")
return [trialValue, nonce]
def run(target, initialHash): def run(target, initialHash):
target = int(target) target = int(target)
if safeConfigGetBoolean('bitmessagesettings', 'opencl') and openclpow.has_opencl():
# trialvalue1, nonce1 = _doGPUPoW(target, initialHash)
# trialvalue, nonce = _doFastPoW(target, initialHash)
# print "GPU: %s, %s" % (trialvalue1, nonce1)
# print "Fast: %s, %s" % (trialvalue, nonce)
# return [trialvalue, nonce]
try:
return _doGPUPoW(target, initialHash)
except:
pass # fallback
if bmpow:
try:
return _doCPoW(target, initialHash)
except:
pass # fallback
if frozen == "macosx_app" or not frozen: if frozen == "macosx_app" or not frozen:
return _doFastPoW(target, initialHash) # on my (Peter Surda) Windows 10, Windows Defender
else: # does not like this and fights with PyBitmessage
return _doSafePoW(target, initialHash) # over CPU, resulting in very slow PoW
# added on 2015-11-29: multiprocesing.freeze_support() doesn't help
try:
return _doFastPoW(target, initialHash)
except:
pass #fallback
return _doSafePoW(target, initialHash)

View File

@ -1,6 +1,6 @@
from __future__ import division from __future__ import division
softwareVersion = '0.4.4' softwareVersion = '0.5.5'
verbose = 1 verbose = 1
maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # This is obsolete with the change to protocol v3 but the singleCleaner thread still hasn't been updated so we need this a little longer. maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # This is obsolete with the change to protocol v3 but the singleCleaner thread still hasn't been updated so we need this a little longer.
lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time. lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time.
@ -32,6 +32,7 @@ import highlevelcrypto
import shared import shared
#import helper_startup #import helper_startup
from helper_sql import * from helper_sql import *
from helper_threading import *
config = ConfigParser.SafeConfigParser() config = ConfigParser.SafeConfigParser()
@ -83,7 +84,7 @@ lastTimeWeResetBytesSent = 0 # used for the bandwidth rate limit
sendDataLock = threading.Lock() # used for the bandwidth rate limit sendDataLock = threading.Lock() # used for the bandwidth rate limit
receiveDataLock = threading.Lock() # used for the bandwidth rate limit receiveDataLock = threading.Lock() # used for the bandwidth rate limit
daemon = False daemon = False
inventorySets = {} # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it every couple hours. inventorySets = {1: set()} # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it every couple hours.
needToWriteKnownNodesToDisk = False # If True, the singleCleaner will write it to disk eventually. needToWriteKnownNodesToDisk = False # If True, the singleCleaner will write it to disk eventually.
maximumLengthOfTimeToBotherResendingMessages = 0 maximumLengthOfTimeToBotherResendingMessages = 0
objectProcessorQueue = Queue.Queue( objectProcessorQueue = Queue.Queue(
@ -116,10 +117,17 @@ frozen = getattr(sys,'frozen', None)
# security. # security.
trustedPeer = None trustedPeer = None
# For UPnP
extPort = None
#Compiled struct for packing/unpacking headers #Compiled struct for packing/unpacking headers
#New code should use CreatePacket instead of Header.pack #New code should use CreatePacket instead of Header.pack
Header = Struct('!L12sL4s') Header = Struct('!L12sL4s')
#Service flags
NODE_NETWORK = 1
NODE_SSL = 2
#Create a packet #Create a packet
def CreatePacket(command, payload=''): def CreatePacket(command, payload=''):
payload_length = len(payload) payload_length = len(payload)
@ -135,16 +143,26 @@ def isInSqlInventory(hash):
return queryreturn != [] return queryreturn != []
def encodeHost(host): def encodeHost(host):
if host.find(':') == -1: if host.find('.onion') > -1:
return '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x7F\x00\x00\x01'
elif host.find(':') == -1:
return '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + \ return '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + \
socket.inet_aton(host) socket.inet_aton(host)
else: else:
return socket.inet_pton(socket.AF_INET6, host) return socket.inet_pton(socket.AF_INET6, host)
def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): def haveSSL(server = False):
# python < 2.7.9's ssl library does not support ECDSA server due to missing initialisation of available curves, but client works ok
if server == False:
return True
elif sys.version_info >= (2,7,9):
return True
return False
def assembleVersionMessage(remoteHost, remotePort, myStreamNumber, server = False):
payload = '' payload = ''
payload += pack('>L', 3) # protocol version. payload += pack('>L', 3) # protocol version.
payload += pack('>q', 1) # bitflags of the services I offer. payload += pack('>q', NODE_NETWORK|(NODE_SSL if haveSSL(server) else 0)) # bitflags of the services I offer.
payload += pack('>q', int(time.time())) payload += pack('>q', int(time.time()))
payload += pack( payload += pack(
@ -155,8 +173,10 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber):
payload += pack('>q', 1) # bitflags of the services I offer. payload += pack('>q', 1) # bitflags of the services I offer.
payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack( payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack(
'>L', 2130706433) # = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used. '>L', 2130706433) # = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used.
payload += pack('>H', shared.config.getint( if safeConfigGetBoolean('bitmessagesettings', 'upnp') and extPort:
'bitmessagesettings', 'port')) payload += pack('>H', extPort)
else:
payload += pack('>H', shared.config.getint('bitmessagesettings', 'port'))
random.seed() random.seed()
payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf
@ -217,6 +237,15 @@ def lookupAppdataFolder():
pass pass
dataFolder = dataFolder + '/' dataFolder = dataFolder + '/'
return dataFolder return dataFolder
def codePath():
if frozen == "macosx_app":
codePath = os.environ.get("RESOURCEPATH")
elif frozen: # windows
codePath = sys._MEIPASS
else:
codePath = os.path.dirname(__file__)
return codePath
def isAddressInMyAddressBook(address): def isAddressInMyAddressBook(address):
queryreturn = sqlQuery( queryreturn = sqlQuery(
@ -383,7 +412,16 @@ def doCleanShutdown():
# Wait long enough to guarantee that any running proof of work worker threads will check the # Wait long enough to guarantee that any running proof of work worker threads will check the
# shutdown variable and exit. If the main thread closes before they do then they won't stop. # shutdown variable and exit. If the main thread closes before they do then they won't stop.
time.sleep(.25) time.sleep(.25)
from class_outgoingSynSender import outgoingSynSender
for thread in threading.enumerate():
if thread.isAlive() and isinstance(thread, StoppableThread):
thread.stopThread()
for thread in threading.enumerate():
if thread is not threading.currentThread() and isinstance(thread, StoppableThread) and not isinstance(thread, outgoingSynSender):
logger.debug("Waiting for thread %s", thread.name)
thread.join()
if safeConfigGetBoolean('bitmessagesettings','daemon'): if safeConfigGetBoolean('bitmessagesettings','daemon'):
logger.info('Clean shutdown complete.') logger.info('Clean shutdown complete.')

View File

@ -3,22 +3,26 @@
import sys import sys
import os import os
import errno import errno
import tempfile import shared
from multiprocessing import Process from multiprocessing import Process
class singleinstance: class singleinstance:
""" """
Implements a single instance application by creating a lock file based on the full path to the script file. Implements a single instance application by creating a lock file at appdata.
This is based upon the singleton class from tendo https://github.com/pycontribs/tendo This is based upon the singleton class from tendo https://github.com/pycontribs/tendo
which is under the Python Software Foundation License version 2 which is under the Python Software Foundation License version 2
""" """
def __init__(self, flavor_id=""): def __init__(self, flavor_id="", daemon=False):
import sys import sys
self.initialized = False self.initialized = False
basename = os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace("/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock' self.daemon = daemon;
self.lockfile = os.path.normpath(tempfile.gettempdir() + '/' + basename) self.lockfile = os.path.normpath(os.path.join(shared.appdata, 'singleton%s.lock' % flavor_id))
if not self.daemon:
# Tells the already running (if any) application to get focus.
import bitmessageqt
bitmessageqt.init()
if sys.platform == 'win32': if sys.platform == 'win32':
try: try:

View File

@ -155,7 +155,7 @@ class socksocket(socket.socket):
""" """
self.__proxy = (proxytype, addr, port, rdns, username, password) self.__proxy = (proxytype, addr, port, rdns, username, password)
def __negotiatesocks5(self, destaddr, destport): def __negotiatesocks5(self):
"""__negotiatesocks5(self,destaddr,destport) """__negotiatesocks5(self,destaddr,destport)
Negotiates a connection through a SOCKS5 server. Negotiates a connection through a SOCKS5 server.
""" """
@ -200,6 +200,8 @@ class socksocket(socket.socket):
raise Socks5AuthError((2, _socks5autherrors[2])) raise Socks5AuthError((2, _socks5autherrors[2]))
else: else:
raise GeneralProxyError((1, _generalerrors[1])) raise GeneralProxyError((1, _generalerrors[1]))
def __connectsocks5(self, destaddr, destport):
# Now we can request the actual connection # Now we can request the actual connection
req = struct.pack('BBB', 0x05, 0x01, 0x00) req = struct.pack('BBB', 0x05, 0x01, 0x00)
# If the given destination address is an IP address, we'll # If the given destination address is an IP address, we'll
@ -247,6 +249,37 @@ class socksocket(socket.socket):
else: else:
self.__proxypeername = (destaddr, destport) self.__proxypeername = (destaddr, destport)
def __resolvesocks5(self, host):
# Now we can request the actual connection
req = struct.pack('BBB', 0x05, 0xF0, 0x00)
req += chr(0x03).encode() + chr(len(host)).encode() + host
req = req + struct.pack(">H", 8444)
self.sendall(req)
# Get the response
ip = ""
resp = self.__recvall(4)
if resp[0:1] != chr(0x05).encode():
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
elif resp[1:2] != chr(0x00).encode():
# Connection failed
self.close()
if ord(resp[1:2])<=8:
raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else:
raise Socks5Error((9, _socks5errors[9]))
# Get the bound address/port
elif resp[3:4] == chr(0x01).encode():
ip = socket.inet_ntoa(self.__recvall(4))
elif resp[3:4] == chr(0x03).encode():
resp = resp + self.recv(1)
ip = self.__recvall(ord(resp[4:5]))
else:
self.close()
raise GeneralProxyError((1,_generalerrors[1]))
boundport = struct.unpack(">H", self.__recvall(2))[0]
return ip
def getproxysockname(self): def getproxysockname(self):
"""getsockname() -> address info """getsockname() -> address info
Returns the bound IP address and port number at the proxy. Returns the bound IP address and port number at the proxy.
@ -361,7 +394,8 @@ class socksocket(socket.socket):
else: else:
portnum = 1080 portnum = 1080
_orgsocket.connect(self, (self.__proxy[1], portnum)) _orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatesocks5(destpair[0], destpair[1]) self.__negotiatesocks5()
self.__connectsocks5(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_SOCKS4: elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
if self.__proxy[2] != None: if self.__proxy[2] != None:
portnum = self.__proxy[2] portnum = self.__proxy[2]
@ -380,3 +414,15 @@ class socksocket(socket.socket):
_orgsocket.connect(self, (destpair[0], destpair[1])) _orgsocket.connect(self, (destpair[0], destpair[1]))
else: else:
raise GeneralProxyError((4, _generalerrors[4])) raise GeneralProxyError((4, _generalerrors[4]))
def resolve(self, host):
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
if self.__proxy[2] != None:
portnum = self.__proxy[2]
else:
portnum = 1080
_orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatesocks5()
return self.__resolvesocks5(host)
else:
return None

15
src/sslkeys/cert.pem Normal file
View File

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICWDCCAcGgAwIBAgIJAJs5yni/cDh5MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTEzMDk1NTU3WhcNMTUxMTE0MDk1NTU3WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQCg8XkFpIAYsTSBealTubvu4dzpMnnAOwANG5K9TJeclG9O65cmKWpH8k3hNDif
xagIAI8UanBsQo6SQrK1Iby2kz6DCKmySO1OwoNOOF0Ok31N+5aWsQvYF1wLbk2m
Ti/CSLWBgL25ywCCiP3Mgr+krapT4TrfvF4gCchUdcxMQQIDAQABo1AwTjAdBgNV
HQ4EFgQUWuFUJQC6zu6OTDgHZzhfZxsgJOMwHwYDVR0jBBgwFoAUWuFUJQC6zu6O
TDgHZzhfZxsgJOMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAT1I/x
GbsYAE4pM4sVQrcuz7jLwr3k5Zve0z4WKR41W17Nc44G3DyLbkTWYESLfAYsivkx
tRRtYTtJm1qmTPtedXQJK+wJGNHCWRfwSB2CYwmO7+C2rYYzkFndN68kB6RJmyOr
eCX+9vkbQqgh7KfiNquJxCfMSDfhA2RszU43jg==
-----END CERTIFICATE-----

16
src/sslkeys/key.pem Normal file
View File

@ -0,0 +1,16 @@
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKDxeQWkgBixNIF5
qVO5u+7h3OkyecA7AA0bkr1Ml5yUb07rlyYpakfyTeE0OJ/FqAgAjxRqcGxCjpJC
srUhvLaTPoMIqbJI7U7Cg044XQ6TfU37lpaxC9gXXAtuTaZOL8JItYGAvbnLAIKI
/cyCv6StqlPhOt+8XiAJyFR1zExBAgMBAAECgYEAmd2hpQpayMCJgQsOHhRgnoXi
jDOMgIInj2CADmguPi0OqTXEoGBR0ozNdfNV+zGdbmESaSNFbcrHwP7xGQgzABlv
5ANLgBYrHnW/oFCCuw4Lj/CAAHRA4its+2wzf13BYoVitDiYBt3JMRqwLV03aHyy
Oqhvt2nVicz85+HERj0CQQDMJAPUIyOQLO+BPC5MsuxyQFJgie0aB5swumxanOv4
J8GIvulNEJMG/dq+h/x4paV2LGDlUAOsBUmjXfTPMQAHAkEAydQtYorqYqhFZWWD
3lUMAoa8pGb6BfNXUqxdH0H8fk6B7OxYPpvwm7ce1lD1Oje3/+rMnn8i6A1p9HUy
l9wvdwJAdhxIUs7Z3qsBD8bgCuRixV/NyalDk5HfCnxyAKNWK8fkw9ehaEM0rhDm
JOLNAojkiND4ZvS6iyasCmdsIwx4tQJAAV+eR3NmkPFQN5ZvRU4S3NmJ4xyISw4S
5A8kOxg53aovHCunlhV9l7GxVggLAzBp4iX46oM2+5lLxUwe4gWvlQJBAK0IR8bB
85bKZ+M/O8rbs9kQHjx6GCbbDxH+qbIKkNcvLUvMgwwIFKiwqX+Tedtu2xET0mQM
9tEE5eMBOJ8GrxQ=
-----END PRIVATE KEY-----

View File

@ -582,8 +582,8 @@ It is important that you back up this file. Would you like to open the file now?
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2502"/> <location filename="../bitmessageqt/__init__.py" line="2502"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation>تم نقل المادة لسلة المهملات، لا يتوفر واجهة مستخدم لإظهار سلة المهملات حالياً، و لكن يمكنك إيجاد الرسالة المحذوفة على القرص الصلب إذا أردت استرجاعها.</translation> <translation>تم نقل المادة لسلة المهملات.</translation>
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2596"/> <location filename="../bitmessageqt/__init__.py" line="2596"/>

View File

@ -556,8 +556,8 @@ Je důležité si tento soubor zazálohovat. Přejete si tento soubor nyní otev
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2838"/> <location filename="../bitmessageqt/__init__.py" line="2838"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation>Položky byly přesunuty do koše. Koš nemá uživatelské rozhraní, ale jeho obsah je stále na disku, pro případ že ho nutně potřebujete obnovit.</translation> <translation>Položky byly přesunuty do koše.</translation>
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2818"/> <location filename="../bitmessageqt/__init__.py" line="2818"/>

View File

@ -491,8 +491,8 @@ Es ist wichtig, dass Sie diese Datei sichern. Möchten Sie die datei jetzt öffn
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2692"/> <location filename="../bitmessageqt/__init__.py" line="2692"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation>Objekt in den Papierkorb verschoben. Es gibt kein Benutzerinterface für den Papierkorb, aber die Daten sind noch auf Ihrer Festplatte wenn Sie sie wirklich benötigen.</translation> <translation>Objekt in den Papierkorb verschoben.</translation>
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2672"/> <location filename="../bitmessageqt/__init__.py" line="2672"/>
@ -905,7 +905,7 @@ p, li { white-space: pre-wrap; }
</message> </message>
<message> <message>
<location filename="../bitmessagemain.py" line="3844"/> <location filename="../bitmessagemain.py" line="3844"/>
<source>Broacasting the public key request. This program will auto-retry if they are offline.</source> <source>Broadcasting the public key request. This program will auto-retry if they are offline.</source>
<translation type="obsolete">Anfrage für den Verschlüsselungscode versendet (wird automatisch periodisch neu verschickt).</translation> <translation type="obsolete">Anfrage für den Verschlüsselungscode versendet (wird automatisch periodisch neu verschickt).</translation>
</message> </message>
<message> <message>

View File

@ -551,7 +551,7 @@ It is important that you back up this file. Would you like to open the file now?
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2692"/> <location filename="../bitmessageqt/__init__.py" line="2692"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>

View File

@ -491,8 +491,8 @@ Estas grava ke vi faru savkopion de tiu dosiero. Ĉu vi volas malfermi la dosier
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2692"/> <location filename="../bitmessageqt/__init__.py" line="2692"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation>Movis elementojn al rubujo. Ne estas fasado por rigardi vian rubujon, sed ankoraŭ estas sur disko se vi esperas ĝin retrovi.</translation> <translation>Movis elementojn al rubujo.</translation>
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2672"/> <location filename="../bitmessageqt/__init__.py" line="2672"/>
@ -899,7 +899,7 @@ p, li { white-space: pre-wrap; }
</message> </message>
<message> <message>
<location filename="../bitmessagemain.py" line="3844"/> <location filename="../bitmessagemain.py" line="3844"/>
<source>Broacasting the public key request. This program will auto-retry if they are offline.</source> <source>Broadcasting the public key request. This program will auto-retry if they are offline.</source>
<translation type="obsolete">Anfrage für den Verschlüsselungscode versendet (wird automatisch periodisch neu verschickt).</translation> <translation type="obsolete">Anfrage für den Verschlüsselungscode versendet (wird automatisch periodisch neu verschickt).</translation>
</message> </message>
<message> <message>

View File

@ -573,8 +573,8 @@ Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l&apos;o
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2692"/> <location filename="../bitmessageqt/__init__.py" line="2692"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation>Messages déplacés dans la corbeille. Il n&apos;y a pas d&apos;interface utilisateur pour voir votre corbeille, mais ils sont toujours présents sur le disque si vous souhaitez les récupérer.</translation> <translation>Messages déplacés dans la corbeille.</translation>
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2769"/> <location filename="../bitmessageqt/__init__.py" line="2769"/>

View File

@ -550,8 +550,8 @@ It is important that you back up this file. Would you like to open the file now?
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2502"/> <location filename="../bitmessageqt/__init__.py" line="2502"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2476"/> <location filename="../bitmessageqt/__init__.py" line="2476"/>

View File

@ -552,7 +552,7 @@ It is important that you back up this file. Would you like to open the file now?
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2692"/> <location filename="../bitmessageqt/__init__.py" line="2692"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>

View File

@ -573,8 +573,8 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du &#xE5;pne denne
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2692"/> <location filename="../bitmessageqt/__init__.py" line="2692"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation>Kastet innholdet. Det finnes ikke noe brukergrensesnitt enda for kastet innhold, men ingenting er slettet enda. Alt ligger fortsatt p&#xE5; disken hvis du er interessert i &#xE5; f&#xE5; det tilbake.</translation> <translation>Kastet innholdet.</translation>
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2769"/> <location filename="../bitmessageqt/__init__.py" line="2769"/>

View File

@ -495,8 +495,8 @@ It is important that you back up this file. Would you like to open the file now?
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2692"/> <location filename="../bitmessageqt/__init__.py" line="2692"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation>Удалено в корзину. Чтобы попасть в корзину, Вам нужно будет найти файл корзины на диске.</translation> <translation>Удалено в корзину.</translation>
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2672"/> <location filename="../bitmessageqt/__init__.py" line="2672"/>

View File

@ -861,8 +861,8 @@ It is important that you back up this file. Would you like to open the file now?
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2750"/> <location filename="../bitmessageqt/__init__.py" line="2750"/>
<source>Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back.</source> <source>Moved items to trash.</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../bitmessageqt/__init__.py" line="2730"/> <location filename="../bitmessageqt/__init__.py" line="2730"/>
@ -1065,7 +1065,7 @@ Receiver&apos;s required difficulty: %1 and %2</source>
</message> </message>
<message> <message>
<location filename="../class_singleWorker.py" line="928"/> <location filename="../class_singleWorker.py" line="928"/>
<source>Broacasting the public key request. This program will auto-retry if they are offline.</source> <source>Broadcasting the public key request. This program will auto-retry if they are offline.</source>
<translation>广. 线, .</translation> <translation>广. 线, .</translation>
</message> </message>
<message> <message>

275
src/upnp.py Normal file
View File

@ -0,0 +1,275 @@
# A simple upnp module to forward port for BitMessage
# Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port
import httplib
from random import randint
import socket
from struct import unpack, pack
import threading
import time
from helper_threading import *
import shared
import tr
def createRequestXML(service, action, arguments=[]):
from xml.dom.minidom import Document
doc = Document()
# create the envelope element and set its attributes
envelope = doc.createElementNS('', 's:Envelope')
envelope.setAttribute('xmlns:s', 'http://schemas.xmlsoap.org/soap/envelope/')
envelope.setAttribute('s:encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/')
# create the body element
body = doc.createElementNS('', 's:Body')
# create the function element and set its attribute
fn = doc.createElementNS('', 'u:%s' % action)
fn.setAttribute('xmlns:u', 'urn:schemas-upnp-org:service:%s' % service)
# setup the argument element names and values
# using a list of tuples to preserve order
# container for created nodes
argument_list = []
# iterate over arguments, create nodes, create text nodes,
# append text nodes to nodes, and finally add the ready product
# to argument_list
for k, v in arguments:
tmp_node = doc.createElement(k)
tmp_text_node = doc.createTextNode(v)
tmp_node.appendChild(tmp_text_node)
argument_list.append(tmp_node)
# append the prepared argument nodes to the function element
for arg in argument_list:
fn.appendChild(arg)
# append function element to the body element
body.appendChild(fn)
# append body element to envelope element
envelope.appendChild(body)
# append envelope element to document, making it the root element
doc.appendChild(envelope)
# our tree is ready, conver it to a string
return doc.toxml()
class UPnPError(Exception):
def __init__(self, message):
self.message
class Router:
name = ""
path = ""
address = None
routerPath = None
extPort = None
def __init__(self, ssdpResponse, address):
import urllib2
from xml.dom.minidom import parseString
from urlparse import urlparse
import pprint
from debug import logger
self.address = address
row = ssdpResponse.split('\r\n')
header = {}
for i in range(1, len(row)):
part = row[i].split(': ')
if len(part) == 2:
header[part[0].lower()] = part[1]
try:
self.routerPath = urlparse(header['location'])
if not self.routerPath or not hasattr(self.routerPath, "hostname"):
logger.error ("UPnP: no hostname: %s", header['location'])
except KeyError:
logger.error ("UPnP: missing location header")
# get the profile xml file and read it into a variable
directory = urllib2.urlopen(header['location']).read()
# create a DOM object that represents the `directory` document
dom = parseString(directory)
self.name = dom.getElementsByTagName('friendlyName')[0].childNodes[0].data
# find all 'serviceType' elements
service_types = dom.getElementsByTagName('serviceType')
for service in service_types:
if service.childNodes[0].data.find('WANIPConnection') > 0:
self.path = service.parentNode.getElementsByTagName('controlURL')[0].childNodes[0].data
# get local IP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
logger.debug("Connecting to %s:%i", self.address, self.routerPath.port)
s.connect ((self.address, self.routerPath.port))
except:
pass
self.localAddress = s.getsockname()[0]
logger.debug("Local IP: %s", self.localAddress)
try:
s.shutdown(socket.SHUT_RDWR)
s.close()
except:
pass
def AddPortMapping(self, externalPort, internalPort, internalClient, protocol, description, leaseDuration = 0, enabled = 1):
from debug import logger
resp = self.soapRequest('WANIPConnection:1', 'AddPortMapping', [
('NewExternalPort', str(externalPort)),
('NewProtocol', protocol),
('NewInternalPort', str(internalPort)),
('NewInternalClient', internalClient),
('NewEnabled', str(enabled)),
('NewPortMappingDescription', str(description)),
('NewLeaseDuration', str(leaseDuration))
])
self.extPort = externalPort
logger.info("Successfully established UPnP mapping for %s:%i on external port %i", internalClient, internalPort, externalPort)
return resp
def DeletePortMapping(self, externalPort, protocol):
from debug import logger
resp = self.soapRequest('WANIPConnection:1', 'DeletePortMapping', [
('NewExternalPort', str(externalPort)),
('NewProtocol', protocol),
])
logger.info("Removed UPnP mapping on external port %i", externalPort)
return resp
def GetExternalIPAddress(self):
from xml.dom.minidom import parseString
resp = self.soapRequest('WANIPConnection:1', 'GetExternalIPAddress')
dom = parseString(resp)
return dom.getElementsByTagName('NewExternalIPAddress')[0].childNodes[0].data
def soapRequest(self, service, action, arguments=[]):
from xml.dom.minidom import parseString
from debug import logger
conn = httplib.HTTPConnection(self.routerPath.hostname, self.routerPath.port)
conn.request(
'POST',
self.path,
createRequestXML(service, action, arguments),
{
'SOAPAction': '"urn:schemas-upnp-org:service:%s#%s"' % (service, action),
'Content-Type': 'text/xml'
}
)
resp = conn.getresponse().read()
dom = parseString(resp)
errinfo = dom.getElementsByTagName('errorDescription')
if len(errinfo) > 0:
logger.error("UPnP error: %s", resp)
raise UPnPError(errinfo[0].childNodes[0].data)
return resp
class uPnPThread(threading.Thread, StoppableThread):
def __init__ (self):
threading.Thread.__init__(self, name="uPnPThread")
self.localPort = shared.config.getint('bitmessagesettings', 'port')
self.extPort = None
self.routers = []
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
self.sock.settimeout(2)
self.sendSleep = 60
self.initStop()
def run(self):
from debug import logger
logger.debug("Starting UPnP thread")
lastSent = 0
while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'):
if time.time() - lastSent > self.sendSleep and len(self.routers) == 0:
self.sendSearchRouter()
lastSent = time.time()
try:
while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'):
resp,(ip,port) = self.sock.recvfrom(1000)
if resp is None:
continue
newRouter = Router(resp, ip)
for router in self.routers:
if router.location == newRouter.location:
break
else:
logger.debug("Found UPnP router at %s", ip)
self.routers.append(newRouter)
self.createPortMapping(newRouter)
shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping established')))
break
except socket.timeout as e:
pass
except:
logger.error("Failure running UPnP router search.", exc_info=True)
for router in self.routers:
if router.extPort is None:
self.createPortMapping(router)
try:
self.sock.shutdown(socket.SHUT_RDWR)
except:
pass
try:
self.sock.close()
except:
pass
deleted = False
for router in self.routers:
if router.extPort is not None:
deleted = True
self.deletePortMapping(router)
shared.extPort = None
if deleted:
shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping removed')))
logger.debug("UPnP thread done")
def sendSearchRouter(self):
from debug import logger
SSDP_ADDR = "239.255.255.250"
SSDP_PORT = 1900
SSDP_MX = 2
SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \
"HOST: %s:%d\r\n" % (SSDP_ADDR, SSDP_PORT) + \
"MAN: \"ssdp:discover\"\r\n" + \
"MX: %d\r\n" % (SSDP_MX, ) + \
"ST: %s\r\n" % (SSDP_ST, ) + "\r\n"
try:
logger.debug("Sending UPnP query")
self.sock.sendto(ssdpRequest, (SSDP_ADDR, SSDP_PORT))
except:
logger.exception("UPnP send query failed")
def createPortMapping(self, router):
from debug import logger
for i in range(50):
try:
routerIP, = unpack('>I', socket.inet_aton(router.address))
localIP = router.localAddress
if i == 0:
extPort = self.localPort # try same port first
else:
extPort = randint(32767, 65535)
logger.debug("Requesting UPnP mapping for %s:%i on external port %i", localIP, self.localPort, extPort)
router.AddPortMapping(extPort, self.localPort, localIP, 'TCP', 'BitMessage')
shared.extPort = extPort
break
except UPnPError:
logger.debug("UPnP error: ", exc_info=True)
def deletePortMapping(self, router):
router.DeletePortMapping(router.extPort, 'TCP')