commit
4f461c61b0
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,4 +5,5 @@ src/build
|
||||||
src/dist
|
src/dist
|
||||||
src/.project
|
src/.project
|
||||||
src/.pydevproject
|
src/.pydevproject
|
||||||
src/.settings/
|
src/.settings/
|
||||||
|
*.dll
|
|
@ -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)
|
||||||
|
|
|
@ -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
2
osx.sh
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
24
src/api.py
24
src/api.py
|
@ -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
1778
src/bitmessagecli.py
Normal 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()
|
|
@ -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:
|
||||||
|
|
|
@ -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
222
src/bitmessageqt/account.py
Normal 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)
|
|
@ -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_())
|
||||||
|
|
||||||
|
|
|
@ -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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
|
||||||
p, li { white-space: pre-wrap; }
|
|
||||||
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;">
|
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
|
p, li { white-space: pre-wrap; }
|
||||||
|
</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html></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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
|
p, li { white-space: pre-wrap; }
|
||||||
|
</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html></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>
|
||||||
|
|
64
src/bitmessageqt/emailgateway.py
Normal file
64
src/bitmessageqt/emailgateway.py
Normal 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))
|
||||||
|
|
157
src/bitmessageqt/emailgateway.ui
Normal file
157
src/bitmessageqt/emailgateway.ui
Normal 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>
|
303
src/bitmessageqt/foldertree.py
Normal file
303
src/bitmessageqt/foldertree.py
Normal 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 + ")")
|
84
src/bitmessageqt/migrationwizard.py
Normal file
84
src/bitmessageqt/migrationwizard.py
Normal 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()
|
354
src/bitmessageqt/newaddresswizard.py
Normal file
354
src/bitmessageqt/newaddresswizard.py
Normal 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 "deterministic" 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()
|
|
@ -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))
|
||||||
|
|
|
@ -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>
|
||||||
|
|
79
src/bitmessageqt/settingsmixin.py
Normal file
79
src/bitmessageqt/settingsmixin.py
Normal 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
106
src/bitmessageqt/utils.py
Normal 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
20
src/bitmsghash/Makefile
Normal 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
|
||||||
|
|
276
src/bitmsghash/bitmsghash.cl
Normal file
276
src/bitmsghash/bitmsghash.cl
Normal 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
145
src/bitmsghash/bitmsghash.cpp
Normal file
145
src/bitmsghash/bitmsghash.cpp
Normal 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;
|
||||||
|
}
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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.'
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 ''')
|
||||||
|
|
40
src/debug.py
40
src/debug.py
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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.'
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
10
src/helper_threading.py
Normal 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
93
src/openclpow.py
Normal 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)
|
||||||
|
|
|
@ -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)
|
|
@ -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.')
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
15
src/sslkeys/cert.pem
Normal 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
16
src/sslkeys/key.pem
Normal 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-----
|
|
@ -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"/>
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -573,8 +573,8 @@ Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l'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'y a pas d'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"/>
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -573,8 +573,8 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du å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å disken hvis du er interessert i å få 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"/>
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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'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
275
src/upnp.py
Normal 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')
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user