commit
4f461c61b0
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ src/dist
|
|||
src/.project
|
||||
src/.pydevproject
|
||||
src/.settings/
|
||||
*.dll
|
|
@ -19,6 +19,14 @@ pseudo-mailing list:
|
|||
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
|
||||
----------
|
||||
* [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."
|
||||
|
||||
export PYBITMESSAGEVERSION=$1
|
||||
|
||||
cd src && python2.7 build_osx.py py2app
|
||||
|
||||
if [[ $? = "0" ]]; then
|
||||
|
|
|
@ -55,7 +55,7 @@ def decodeBase58(string, alphabet=ALPHABET):
|
|||
|
||||
def encodeVarint(integer):
|
||||
if integer < 0:
|
||||
print 'varint cannot be < 0'
|
||||
logger.error('varint cannot be < 0')
|
||||
raise SystemExit
|
||||
if integer < 253:
|
||||
return pack('>B',integer)
|
||||
|
@ -66,7 +66,7 @@ def encodeVarint(integer):
|
|||
if integer >= 4294967296 and integer < 18446744073709551616:
|
||||
return pack('>B',255) + pack('>Q',integer)
|
||||
if integer >= 18446744073709551616:
|
||||
print 'varint cannot be >= 18446744073709551616'
|
||||
logger.error('varint cannot be >= 18446744073709551616')
|
||||
raise SystemExit
|
||||
|
||||
class varintDecodeError(Exception):
|
||||
|
@ -185,25 +185,25 @@ def decodeAddress(address):
|
|||
try:
|
||||
addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9])
|
||||
except varintDecodeError as e:
|
||||
print e
|
||||
logger.error(str(e))
|
||||
status = 'varintmalformed'
|
||||
return status,0,0,""
|
||||
#print 'addressVersionNumber', addressVersionNumber
|
||||
#print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber
|
||||
|
||||
if addressVersionNumber > 4:
|
||||
print 'cannot decode address version numbers this high'
|
||||
logger.error('cannot decode address version numbers this high')
|
||||
status = 'versiontoohigh'
|
||||
return status,0,0,""
|
||||
elif addressVersionNumber == 0:
|
||||
print 'cannot decode address version numbers of zero.'
|
||||
logger.error('cannot decode address version numbers of zero.')
|
||||
status = 'versiontoohigh'
|
||||
return status,0,0,""
|
||||
|
||||
try:
|
||||
streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:])
|
||||
except varintDecodeError as e:
|
||||
print e
|
||||
logger.error(str(e))
|
||||
status = 'varintmalformed'
|
||||
return status,0,0,""
|
||||
#print streamNumber
|
||||
|
|
22
src/api.py
22
src/api.py
|
@ -12,7 +12,7 @@ if __name__ == "__main__":
|
|||
import sys
|
||||
sys.exit(0)
|
||||
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
|
||||
import json
|
||||
|
||||
import shared
|
||||
|
@ -26,7 +26,7 @@ from pyelliptic.openssl import OpenSSL
|
|||
from struct import pack
|
||||
|
||||
# Classes
|
||||
from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute
|
||||
from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute,sqlStoredProcedure
|
||||
from debug import logger
|
||||
|
||||
# Helper Functions
|
||||
|
@ -43,6 +43,13 @@ class APIError(Exception):
|
|||
def __str__(self):
|
||||
return "API Error %04i: %s" % (self.error_number, self.error_message)
|
||||
|
||||
|
||||
class StoppableXMLRPCServer(SimpleXMLRPCServer):
|
||||
def serve_forever(self):
|
||||
while shared.shutdown == 0:
|
||||
self.handle_request()
|
||||
|
||||
|
||||
# This is one of several classes that constitute the API
|
||||
# This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros).
|
||||
# http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/
|
||||
|
@ -174,6 +181,13 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
return data
|
||||
|
||||
def HandleListAddressBookEntries(self, params):
|
||||
if len(params) == 1:
|
||||
label, = params
|
||||
label = self._decode(label, "base64")
|
||||
queryreturn = sqlQuery('''SELECT label, address from addressbook WHERE label = ?''', label)
|
||||
elif len(params) > 1:
|
||||
raise APIError(0, "Too many paremeters, max 1")
|
||||
else:
|
||||
queryreturn = sqlQuery('''SELECT label, address from addressbook''')
|
||||
data = '{"addresses":['
|
||||
for row in queryreturn:
|
||||
|
@ -956,6 +970,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
message, = params
|
||||
shared.UISignalQueue.put(('updateStatusBar', message))
|
||||
|
||||
def HandleDeleteAndVacuum(self, params):
|
||||
sqlStoredProcedure('deleteandvacuume')
|
||||
return 'done'
|
||||
|
||||
handlers = {}
|
||||
handlers['helloWorld'] = HandleHelloWorld
|
||||
|
@ -1006,6 +1023,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
handlers['getMessageDataByDestinationTag'] = HandleGetMessageDataByDestinationHash
|
||||
handlers['clientStatus'] = HandleClientStatus
|
||||
handlers['decodeAddress'] = HandleDecodeAddress
|
||||
handlers['deleteAndVacuum'] = HandleDeleteAndVacuum
|
||||
|
||||
def _handle_request(self, method, params):
|
||||
if (self.handlers.has_key(method)):
|
||||
|
|
1778
src/bitmessagecli.py
Normal file
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
|
||||
import time
|
||||
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
from api import MySimpleXMLRPCRequestHandler
|
||||
from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer
|
||||
from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections
|
||||
|
||||
import shared
|
||||
|
@ -44,7 +43,10 @@ from debug import logger
|
|||
# Helper Functions
|
||||
import helper_bootstrap
|
||||
import helper_generic
|
||||
from helper_threading import *
|
||||
|
||||
# singleton lock instance
|
||||
thisapp = None
|
||||
|
||||
def connectToStream(streamNumber):
|
||||
shared.streamsInWhichIAmParticipating[streamNumber] = 'no data'
|
||||
|
@ -117,13 +119,24 @@ def _fixWinsock():
|
|||
socket.IPV6_V6ONLY = 27
|
||||
|
||||
# This thread, of which there is only one, runs the API.
|
||||
class singleAPI(threading.Thread):
|
||||
|
||||
class singleAPI(threading.Thread, StoppableThread):
|
||||
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):
|
||||
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)
|
||||
se.register_introspection_functions()
|
||||
se.serve_forever()
|
||||
|
@ -139,11 +152,13 @@ if shared.useVeryEasyProofOfWorkForTesting:
|
|||
|
||||
class Main:
|
||||
def start(self, daemon=False):
|
||||
global thisapp
|
||||
|
||||
_fixWinsock()
|
||||
|
||||
shared.daemon = daemon
|
||||
# is the application already running? If yes then exit.
|
||||
thisapp = singleton.singleinstance()
|
||||
thisapp = singleton.singleinstance("", daemon)
|
||||
|
||||
# get curses flag
|
||||
curses = False
|
||||
|
@ -204,6 +219,11 @@ class Main:
|
|||
singleListenerThread.daemon = True # close the main program even if there are threads left
|
||||
singleListenerThread.start()
|
||||
|
||||
if shared.safeConfigGetBoolean('bitmessagesettings','upnp'):
|
||||
import upnp
|
||||
upnpThread = upnp.uPnPThread()
|
||||
upnpThread.start()
|
||||
|
||||
if daemon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False:
|
||||
if curses == False:
|
||||
if not depends.check_pyqt():
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from debug import logger
|
||||
withMessagingMenu = False
|
||||
try:
|
||||
from gi.repository import MessagingMenu
|
||||
|
@ -6,17 +7,38 @@ try:
|
|||
except ImportError:
|
||||
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 *
|
||||
import shared
|
||||
from bitmessageui import *
|
||||
from namecoin import namecoinConnection, ensureNamecoinOptions
|
||||
from newaddressdialog import *
|
||||
from newaddresswizard import *
|
||||
from migrationwizard import *
|
||||
from foldertree import *
|
||||
from addaddressdialog import *
|
||||
from newsubscriptiondialog import *
|
||||
from regenerateaddresses import *
|
||||
from newchandialog import *
|
||||
from specialaddressbehavior import *
|
||||
from emailgateway import *
|
||||
from settings import *
|
||||
import settingsmixin
|
||||
from about import *
|
||||
from help import *
|
||||
from iconglossary import *
|
||||
|
@ -31,143 +53,29 @@ import pickle
|
|||
import platform
|
||||
import textwrap
|
||||
import debug
|
||||
from debug import logger
|
||||
import random
|
||||
import subprocess
|
||||
import string
|
||||
import datetime
|
||||
from helper_sql import *
|
||||
import l10n
|
||||
|
||||
try:
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui 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
|
||||
import openclpow
|
||||
import types
|
||||
from utils import *
|
||||
from collections import OrderedDict
|
||||
from account import *
|
||||
|
||||
def _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):
|
||||
global qtranslator
|
||||
qtranslator = QtCore.QTranslator()
|
||||
translationpath = os.path.join(
|
||||
getattr(sys, '_MEIPASS', sys.path[0]),
|
||||
'translations',
|
||||
'bitmessage_' + locale
|
||||
)
|
||||
translationpath = os.path.join (shared.codePath(), 'translations', 'bitmessage_' + locale)
|
||||
qtranslator.load(translationpath)
|
||||
QtGui.QApplication.installTranslator(qtranslator)
|
||||
|
||||
|
||||
class MyForm(QtGui.QMainWindow):
|
||||
class MyForm(settingsmixin.SMainWindow):
|
||||
|
||||
# sound type constants
|
||||
SOUND_NONE = 0
|
||||
|
@ -183,7 +91,6 @@ class MyForm(QtGui.QMainWindow):
|
|||
# the maximum frequency of message sounds in seconds
|
||||
maxSoundFrequencySec = 60
|
||||
|
||||
str_broadcast_subscribers = '[Broadcast subscribers]'
|
||||
str_chan = '[chan]'
|
||||
|
||||
def init_file_menu(self):
|
||||
|
@ -199,13 +106,11 @@ class MyForm(QtGui.QMainWindow):
|
|||
QtCore.SIGNAL(
|
||||
"triggered()"),
|
||||
self.click_actionRegenerateDeterministicAddresses)
|
||||
QtCore.QObject.connect(self.ui.actionJoinChan, QtCore.SIGNAL(
|
||||
"triggered()"),
|
||||
QtCore.QObject.connect(self.ui.pushButtonAddChan, QtCore.SIGNAL(
|
||||
"clicked()"),
|
||||
self.click_actionJoinChan) # also used for creating chans.
|
||||
QtCore.QObject.connect(self.ui.pushButtonNewAddress, QtCore.SIGNAL(
|
||||
"clicked()"), self.click_NewAddressDialog)
|
||||
QtCore.QObject.connect(self.ui.comboBoxSendFrom, QtCore.SIGNAL(
|
||||
"activated(int)"), self.redrawLabelFrom)
|
||||
QtCore.QObject.connect(self.ui.pushButtonAddAddressBook, QtCore.SIGNAL(
|
||||
"clicked()"), self.click_pushButtonAddAddressBook)
|
||||
QtCore.QObject.connect(self.ui.pushButtonAddSubscription, QtCore.SIGNAL(
|
||||
|
@ -216,10 +121,6 @@ class MyForm(QtGui.QMainWindow):
|
|||
"clicked()"), self.click_pushButtonTTL)
|
||||
QtCore.QObject.connect(self.ui.pushButtonSend, QtCore.SIGNAL(
|
||||
"clicked()"), self.click_pushButtonSend)
|
||||
QtCore.QObject.connect(self.ui.pushButtonLoadFromAddressBook,
|
||||
QtCore.SIGNAL(
|
||||
"clicked()"),
|
||||
self.click_pushButtonLoadFromAddressBook)
|
||||
QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL(
|
||||
"clicked()"), self.click_pushButtonFetchNamecoinID)
|
||||
QtCore.QObject.connect(self.ui.radioButtonBlacklist, QtCore.SIGNAL(
|
||||
|
@ -245,9 +146,16 @@ class MyForm(QtGui.QMainWindow):
|
|||
_translate(
|
||||
"MainWindow", "Add sender to your Address Book"),
|
||||
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(
|
||||
_translate("MainWindow", "Move to Trash"),
|
||||
self.on_action_InboxTrash)
|
||||
self.actionUndeleteTrashedMessage = self.ui.inboxContextMenuToolbar.addAction(
|
||||
_translate("MainWindow", "Undelete"),
|
||||
self.on_action_TrashUndelete)
|
||||
self.actionForceHtml = self.ui.inboxContextMenuToolbar.addAction(
|
||||
_translate(
|
||||
"MainWindow", "View HTML code as formatted text"),
|
||||
|
@ -259,61 +167,97 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.actionMarkUnread = self.ui.inboxContextMenuToolbar.addAction(
|
||||
_translate(
|
||||
"MainWindow", "Mark Unread"), self.on_action_InboxMarkUnread)
|
||||
|
||||
# contextmenu messagelists
|
||||
self.ui.tableWidgetInbox.setContextMenuPolicy(
|
||||
QtCore.Qt.CustomContextMenu)
|
||||
if connectSignal:
|
||||
self.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL(
|
||||
'customContextMenuRequested(const QPoint&)'),
|
||||
self.on_context_menuInbox)
|
||||
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.actionSaveMessageAs)
|
||||
self.popMenuInbox.addAction(self.actionTrashInboxMessage)
|
||||
self.ui.tableWidgetInboxSubscriptions.setContextMenuPolicy(
|
||||
QtCore.Qt.CustomContextMenu)
|
||||
if connectSignal:
|
||||
self.connect(self.ui.tableWidgetInboxSubscriptions, QtCore.SIGNAL(
|
||||
'customContextMenuRequested(const QPoint&)'),
|
||||
self.on_context_menuInbox)
|
||||
self.ui.tableWidgetInboxChans.setContextMenuPolicy(
|
||||
QtCore.Qt.CustomContextMenu)
|
||||
if connectSignal:
|
||||
self.connect(self.ui.tableWidgetInboxChans, QtCore.SIGNAL(
|
||||
'customContextMenuRequested(const QPoint&)'),
|
||||
self.on_context_menuInbox)
|
||||
|
||||
def init_identities_popup_menu(self, connectSignal=True):
|
||||
# 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()
|
||||
# Actions
|
||||
self.actionNew = self.ui.addressContextMenuToolbar.addAction(_translate(
|
||||
"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(
|
||||
_translate(
|
||||
"MainWindow", "Enable"), self.on_action_YourIdentitiesEnable)
|
||||
"MainWindow", "Enable"), self.on_action_Enable)
|
||||
self.actionDisable = self.ui.addressContextMenuToolbar.addAction(
|
||||
_translate(
|
||||
"MainWindow", "Disable"), self.on_action_YourIdentitiesDisable)
|
||||
"MainWindow", "Disable"), self.on_action_Disable)
|
||||
self.actionSetAvatar = self.ui.addressContextMenuToolbar.addAction(
|
||||
_translate(
|
||||
"MainWindow", "Set avatar..."),
|
||||
self.on_action_YourIdentitiesSetAvatar)
|
||||
self.on_action_TreeWidgetSetAvatar)
|
||||
self.actionClipboard = self.ui.addressContextMenuToolbar.addAction(
|
||||
_translate(
|
||||
"MainWindow", "Copy address to clipboard"),
|
||||
self.on_action_YourIdentitiesClipboard)
|
||||
self.on_action_Clipboard)
|
||||
self.actionSpecialAddressBehavior = self.ui.addressContextMenuToolbar.addAction(
|
||||
_translate(
|
||||
"MainWindow", "Special address behavior..."),
|
||||
self.on_action_SpecialAddressBehaviorDialog)
|
||||
self.ui.tableWidgetYourIdentities.setContextMenuPolicy(
|
||||
|
||||
self.ui.treeWidgetChans.setContextMenuPolicy(
|
||||
QtCore.Qt.CustomContextMenu)
|
||||
if connectSignal:
|
||||
self.connect(self.ui.tableWidgetYourIdentities, QtCore.SIGNAL(
|
||||
self.connect(self.ui.treeWidgetChans, QtCore.SIGNAL(
|
||||
'customContextMenuRequested(const QPoint&)'),
|
||||
self.on_context_menuYourIdentities)
|
||||
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)
|
||||
self.on_context_menuChan)
|
||||
|
||||
def init_addressbook_popup_menu(self, connectSignal=True):
|
||||
# Popup menu for the Address Book page
|
||||
|
@ -347,14 +291,6 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL(
|
||||
'customContextMenuRequested(const QPoint&)'),
|
||||
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):
|
||||
# Popup menu for the Subscriptions page
|
||||
|
@ -376,22 +312,13 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.on_action_SubscriptionsDisable)
|
||||
self.actionsubscriptionsSetAvatar = self.ui.subscriptionsContextMenuToolbar.addAction(
|
||||
_translate("MainWindow", "Set avatar..."),
|
||||
self.on_action_SubscriptionsSetAvatar)
|
||||
self.ui.tableWidgetSubscriptions.setContextMenuPolicy(
|
||||
self.on_action_TreeWidgetSetAvatar)
|
||||
self.ui.treeWidgetSubscriptions.setContextMenuPolicy(
|
||||
QtCore.Qt.CustomContextMenu)
|
||||
if connectSignal:
|
||||
self.connect(self.ui.tableWidgetSubscriptions, QtCore.SIGNAL(
|
||||
self.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL(
|
||||
'customContextMenuRequested(const QPoint&)'),
|
||||
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):
|
||||
# Popup menu for the Sent page
|
||||
|
@ -407,12 +334,6 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.actionForceSend = self.ui.sentContextMenuToolbar.addAction(
|
||||
_translate(
|
||||
"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.addAction( self.actionSentClipboard )
|
||||
# self.popMenuSent.addAction( self.actionTrashSentMessage )
|
||||
|
@ -457,6 +378,198 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.popMenuBlacklist.addAction(self.actionBlacklistDisable)
|
||||
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):
|
||||
QtGui.QWidget.__init__(self, parent)
|
||||
self.ui = Ui_MainWindow()
|
||||
|
@ -464,9 +577,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
|
||||
# Ask the user if we may delete their old version 1 addresses if they
|
||||
# have any.
|
||||
configSections = shared.config.sections()
|
||||
for addressInKeysFile in configSections:
|
||||
if addressInKeysFile != 'bitmessagesettings':
|
||||
for addressInKeysFile in getSortedAccounts():
|
||||
status, addressVersionNumber, streamNumber, hash = decodeAddress(
|
||||
addressInKeysFile)
|
||||
if addressVersionNumber == 1:
|
||||
|
@ -500,59 +611,40 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.totalNumberOfBytesReceived = 0
|
||||
self.totalNumberOfBytesSent = 0
|
||||
|
||||
self.ui.labelSendBroadcastWarning.setVisible(False)
|
||||
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.start(2000) # milliseconds
|
||||
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_inbox_popup_menu()
|
||||
self.init_identities_popup_menu()
|
||||
self.init_addressbook_popup_menu()
|
||||
self.init_subscriptions_popup_menu()
|
||||
self.init_chan_popup_menu()
|
||||
self.init_sent_popup_menu()
|
||||
self.init_blacklist_popup_menu()
|
||||
|
||||
# Initialize the user's list of addresses on the 'Your Identities' tab.
|
||||
configSections = shared.config.sections()
|
||||
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)
|
||||
# Initialize the user's list of addresses on the 'Chan' tab.
|
||||
self.rerenderTabTreeChans()
|
||||
|
||||
# Load inbox from messages database file
|
||||
self.loadInbox()
|
||||
# Initialize the user's list of addresses on the 'Messages' tab.
|
||||
self.rerenderTabTreeMessages()
|
||||
|
||||
# Load Sent items from database
|
||||
self.loadSent()
|
||||
# Set welcome message
|
||||
self.ui.textEditInboxMessage.setText(
|
||||
"""
|
||||
Welcome to easy and secure Bitmessage
|
||||
* send messages to other people
|
||||
* send broadcast messages like twitter or
|
||||
* discuss in chan(nel)s with other people
|
||||
"""
|
||||
)
|
||||
|
||||
# Initialize the address book
|
||||
self.rerenderAddressBook()
|
||||
|
@ -563,10 +655,10 @@ class MyForm(QtGui.QMainWindow):
|
|||
# Initialize the inbox search
|
||||
QtCore.QObject.connect(self.ui.inboxSearchLineEdit, QtCore.SIGNAL(
|
||||
"returnPressed()"), self.inboxSearchLineEditPressed)
|
||||
|
||||
# Initialize the sent search
|
||||
QtCore.QObject.connect(self.ui.sentSearchLineEdit, QtCore.SIGNAL(
|
||||
"returnPressed()"), self.sentSearchLineEditPressed)
|
||||
QtCore.QObject.connect(self.ui.inboxSearchLineEditSubscriptions, QtCore.SIGNAL(
|
||||
"returnPressed()"), self.inboxSearchLineEditPressed)
|
||||
QtCore.QObject.connect(self.ui.inboxSearchLineEditChans, QtCore.SIGNAL(
|
||||
"returnPressed()"), self.inboxSearchLineEditPressed)
|
||||
|
||||
# Initialize the Blacklist or Whitelist
|
||||
if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'white':
|
||||
|
@ -574,16 +666,31 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.ui.radioButtonWhitelist.click()
|
||||
self.rerenderBlackWhiteList()
|
||||
|
||||
QtCore.QObject.connect(self.ui.tableWidgetYourIdentities, QtCore.SIGNAL(
|
||||
"itemChanged(QTableWidgetItem *)"), self.tableWidgetYourIdentitiesItemChanged)
|
||||
# Initialize addressbook
|
||||
QtCore.QObject.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL(
|
||||
"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(
|
||||
"itemSelectionChanged ()"), self.tableWidgetInboxItemClicked)
|
||||
QtCore.QObject.connect(self.ui.tableWidgetSent, QtCore.SIGNAL(
|
||||
"itemSelectionChanged ()"), self.tableWidgetSentItemClicked)
|
||||
QtCore.QObject.connect(self.ui.tableWidgetInboxSubscriptions, QtCore.SIGNAL(
|
||||
"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
|
||||
# self.ui.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png"))
|
||||
|
@ -594,13 +701,14 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.numberOfMessagesProcessed = 0
|
||||
self.numberOfBroadcastsProcessed = 0
|
||||
self.numberOfPubkeysProcessed = 0
|
||||
self.unreadCount = 0
|
||||
|
||||
# Set the icon sizes for the identicons
|
||||
identicon_size = 3*7
|
||||
self.ui.tableWidgetInbox.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.tableWidgetSent.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.tableWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.tableWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.treeWidgetChans.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.treeWidgetYourIdentities.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.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
|
||||
|
@ -641,6 +749,8 @@ class MyForm(QtGui.QMainWindow):
|
|||
"rerenderBlackWhiteList()"), self.rerenderBlackWhiteList)
|
||||
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
|
||||
"removeInboxRowByMsgid(PyQt_PyObject)"), self.removeInboxRowByMsgid)
|
||||
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
|
||||
"newVersionAvailable(PyQt_PyObject)"), self.newVersionAvailable)
|
||||
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
|
||||
"displayAlert(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayAlert)
|
||||
self.UISignalThread.start()
|
||||
|
@ -649,6 +759,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
# structures were initialized.
|
||||
|
||||
self.rerenderComboBoxSendFrom()
|
||||
self.rerenderComboBoxSendFromBroadcast()
|
||||
|
||||
# Put the TTL slider in the correct spot
|
||||
TTL = shared.config.getint('bitmessagesettings', 'ttl')
|
||||
|
@ -662,6 +773,8 @@ class MyForm(QtGui.QMainWindow):
|
|||
QtCore.QObject.connect(self.ui.horizontalSliderTTL, QtCore.SIGNAL(
|
||||
"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.
|
||||
try:
|
||||
options = {}
|
||||
|
@ -674,7 +787,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
if nc.test()[0] == 'failed':
|
||||
self.ui.pushButtonFetchNamecoinID.hide()
|
||||
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()
|
||||
|
||||
def updateTTL(self, sliderPosition):
|
||||
|
@ -700,10 +813,6 @@ class MyForm(QtGui.QMainWindow):
|
|||
if not self.actionShow.isChecked():
|
||||
self.hide()
|
||||
else:
|
||||
if sys.platform[0:3] == 'win':
|
||||
self.setWindowFlags(Qt.Window)
|
||||
# else:
|
||||
# self.showMaximized()
|
||||
self.show()
|
||||
self.setWindowState(
|
||||
self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
|
||||
|
@ -781,92 +890,99 @@ class MyForm(QtGui.QMainWindow):
|
|||
# Show the program window and select subscriptions tab
|
||||
def appIndicatorSubscribe(self):
|
||||
self.appIndicatorShow()
|
||||
self.ui.tabWidget.setCurrentIndex(4)
|
||||
self.ui.tabWidget.setCurrentIndex(2)
|
||||
|
||||
# Show the program window and select the address book tab
|
||||
def appIndicatorAddressBook(self):
|
||||
# Show the program window and select channels tab
|
||||
def appIndicatorChannel(self):
|
||||
self.appIndicatorShow()
|
||||
self.ui.tabWidget.setCurrentIndex(5)
|
||||
self.ui.tabWidget.setCurrentIndex(3)
|
||||
|
||||
# Load Sent items from database
|
||||
def loadSent(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"
|
||||
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:
|
||||
where = "toaddress || fromaddress || subject || message"
|
||||
|
||||
sqlStatement = '''
|
||||
SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
|
||||
FROM sent WHERE folder="sent" AND %s LIKE ?
|
||||
ORDER BY lastactiontime
|
||||
''' % (where,)
|
||||
|
||||
while self.ui.tableWidgetSent.rowCount() > 0:
|
||||
self.ui.tableWidgetSent.removeRow(0)
|
||||
|
||||
queryreturn = sqlQuery(sqlStatement, what)
|
||||
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:
|
||||
toAddress, fromAddress, subject, status, ackdata, lastactiontime = row
|
||||
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, "")
|
||||
|
||||
if shared.config.has_section(fromAddress):
|
||||
fromLabel = shared.config.get(fromAddress, 'label')
|
||||
else:
|
||||
fromLabel = fromAddress
|
||||
|
||||
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'))
|
||||
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)
|
||||
self.ui.tableWidgetSent.setItem(0, 0, toAddressItem)
|
||||
items.append(toAddressItem)
|
||||
|
||||
if fromLabel == '':
|
||||
fromLabel = fromAddress
|
||||
fromAddressItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8'))
|
||||
fromAddressItem.setToolTip(unicode(fromLabel, 'utf-8'))
|
||||
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)
|
||||
self.ui.tableWidgetSent.setItem(0, 1, fromAddressItem)
|
||||
items.append(fromAddressItem)
|
||||
|
||||
subjectItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8'))
|
||||
subjectItem.setToolTip(unicode(subject, 'utf-8'))
|
||||
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)
|
||||
self.ui.tableWidgetSent.setItem(0, 2, subjectItem)
|
||||
items.append(subjectItem)
|
||||
|
||||
if status == 'awaitingpubkey':
|
||||
statusText = _translate(
|
||||
|
@ -913,105 +1029,52 @@ class MyForm(QtGui.QMainWindow):
|
|||
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:
|
||||
where = "toaddress || fromaddress || subject || message"
|
||||
|
||||
sqlStatement = '''
|
||||
SELECT msgid, toaddress, fromaddress, subject, received, read
|
||||
FROM inbox WHERE folder="inbox" AND %s LIKE ?
|
||||
ORDER BY received
|
||||
''' % (where,)
|
||||
|
||||
while self.ui.tableWidgetInbox.rowCount() > 0:
|
||||
self.ui.tableWidgetInbox.removeRow(0)
|
||||
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)
|
||||
queryreturn = sqlQuery(sqlStatement, what)
|
||||
for row in queryreturn:
|
||||
msgid, toAddress, fromAddress, subject, received, read = row
|
||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||
try:
|
||||
if toAddress == self.str_broadcast_subscribers:
|
||||
toLabel = self.str_broadcast_subscribers
|
||||
if tableWidget == self.ui.tableWidgetInboxSubscriptions:
|
||||
acct = accountClass(fromAddress)
|
||||
else:
|
||||
toLabel = shared.config.get(toAddress, 'label')
|
||||
except:
|
||||
toLabel = ''
|
||||
if toLabel == '':
|
||||
toLabel = toAddress
|
||||
acct = accountClass(toAddress)
|
||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||
acct.parseMessage(toAddress, fromAddress, subject, "")
|
||||
|
||||
fromLabel = ''
|
||||
if shared.config.has_section(fromAddress):
|
||||
fromLabel = shared.config.get(fromAddress, 'label')
|
||||
|
||||
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)
|
||||
items = []
|
||||
#to
|
||||
to_item = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8'))
|
||||
to_item.setToolTip(unicode(toLabel, 'utf-8'))
|
||||
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))
|
||||
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.setTextColor(AccountColor(toAddress).accountColor())
|
||||
to_item.setIcon(avatarize(toAddress))
|
||||
self.ui.tableWidgetInbox.setItem(0, 0, to_item)
|
||||
items.append(to_item)
|
||||
# from
|
||||
from_item = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8'))
|
||||
from_item.setToolTip(unicode(fromLabel, 'utf-8'))
|
||||
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))
|
||||
if shared.safeConfigGetBoolean(str(fromAddress), 'chan'):
|
||||
from_item.setTextColor(QtGui.QColor(216, 119, 0)) # orange
|
||||
from_item.setTextColor(AccountColor(fromAddress).accountColor())
|
||||
from_item.setIcon(avatarize(fromAddress))
|
||||
self.ui.tableWidgetInbox.setItem(0, 1, from_item)
|
||||
items.append(from_item)
|
||||
# subject
|
||||
subject_item = QtGui.QTableWidgetItem(unicode(subject, 'utf-8'))
|
||||
subject_item.setToolTip(unicode(subject, 'utf-8'))
|
||||
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)
|
||||
self.ui.tableWidgetInbox.setItem(0, 2, subject_item)
|
||||
items.append(subject_item)
|
||||
# time received
|
||||
time_item = myTableWidgetItem(l10n.formatTimestamp(received))
|
||||
time_item.setToolTip(l10n.formatTimestamp(received))
|
||||
|
@ -1021,10 +1084,112 @@ class MyForm(QtGui.QMainWindow):
|
|||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
if not read:
|
||||
time_item.setFont(font)
|
||||
self.ui.tableWidgetInbox.setItem(0, 3, time_item)
|
||||
items.append(time_item)
|
||||
self.addMessageListItem(tableWidget, items)
|
||||
return acct
|
||||
|
||||
self.ui.tableWidgetInbox.sortItems(3, Qt.DescendingOrder)
|
||||
self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent
|
||||
# Load Sent items from database
|
||||
def loadSent(self, tableWidget, account, where="", 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:
|
||||
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 = '''
|
||||
SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
|
||||
FROM sent WHERE ''' + xAddress + '''=? AND folder="sent" AND %s LIKE ?
|
||||
ORDER BY lastactiontime
|
||||
''' % (where,)
|
||||
|
||||
tableWidget.setRowCount(0)
|
||||
acct = None
|
||||
queryreturn = sqlQuery(sqlStatement, account, what)
|
||||
for row in queryreturn:
|
||||
toAddress, fromAddress, subject, status, ackdata, lastactiontime = row
|
||||
self.addMessageListItemSent(tableWidget, toAddress, fromAddress, subject, status, ackdata, lastactiontime)
|
||||
|
||||
tableWidget.setSortingEnabled(False)
|
||||
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:
|
||||
where = "toaddress || fromaddress || subject || message"
|
||||
else:
|
||||
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)
|
||||
|
||||
tableWidget.setRowCount(0)
|
||||
if account is not None:
|
||||
tableWidget.setColumnHidden(0, True)
|
||||
tableWidget.setColumnHidden(1, False)
|
||||
else:
|
||||
tableWidget.setColumnHidden(0, False)
|
||||
tableWidget.setColumnHidden(1, False)
|
||||
tableWidget.setSortingEnabled(False)
|
||||
|
||||
for row in queryreturn:
|
||||
msgfolder, msgid, toAddress, fromAddress, subject, received, read = row
|
||||
self.addMessageListItemInbox(tableWidget, msgfolder, msgid, toAddress, fromAddress, subject, received, read)
|
||||
|
||||
tableWidget.horizontalHeader().setSortIndicator(3, Qt.DescendingOrder)
|
||||
tableWidget.setSortingEnabled(True)
|
||||
tableWidget.keyPressEvent = self.tableWidgetInboxKeyPressEvent
|
||||
|
||||
# create application indicator
|
||||
def appIndicatorInit(self, app):
|
||||
|
@ -1065,11 +1230,11 @@ class MyForm(QtGui.QMainWindow):
|
|||
actionSubscribe.triggered.connect(self.appIndicatorSubscribe)
|
||||
m.addAction(actionSubscribe)
|
||||
|
||||
# Address book
|
||||
actionAddressBook = QtGui.QAction(_translate(
|
||||
"MainWindow", "Address Book"), m, checkable=False)
|
||||
actionAddressBook.triggered.connect(self.appIndicatorAddressBook)
|
||||
m.addAction(actionAddressBook)
|
||||
# Channels
|
||||
actionSubscribe = QtGui.QAction(_translate(
|
||||
"MainWindow", "Channel"), m, checkable=False)
|
||||
actionSubscribe.triggered.connect(self.appIndicatorChannel)
|
||||
m.addAction(actionSubscribe)
|
||||
|
||||
# separator
|
||||
actionSeparator = QtGui.QAction('', m, checkable=False)
|
||||
|
@ -1115,7 +1280,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
for row in queryreturn:
|
||||
toAddress, read = row
|
||||
if not read:
|
||||
if toAddress == self.str_broadcast_subscribers:
|
||||
if toAddress == str_broadcast_subscribers:
|
||||
if self.mmapp.has_source("Subscriptions"):
|
||||
self.mmapp.remove_source("Subscriptions")
|
||||
else:
|
||||
|
@ -1133,8 +1298,8 @@ class MyForm(QtGui.QMainWindow):
|
|||
msgid, toAddress, read = row
|
||||
|
||||
try:
|
||||
if toAddress == self.str_broadcast_subscribers:
|
||||
toLabel = self.str_broadcast_subscribers
|
||||
if toAddress == str_broadcast_subscribers:
|
||||
toLabel = str_broadcast_subscribers
|
||||
else:
|
||||
toLabel = shared.config.get(toAddress, 'label')
|
||||
except:
|
||||
|
@ -1143,7 +1308,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
toLabel = toAddress
|
||||
|
||||
if not read:
|
||||
if toLabel == self.str_broadcast_subscribers:
|
||||
if toLabel == str_broadcast_subscribers:
|
||||
# increment the unread subscriptions
|
||||
unreadSubscriptions = unreadSubscriptions + 1
|
||||
else:
|
||||
|
@ -1179,7 +1344,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
|
||||
# has messageing menu been installed
|
||||
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
|
||||
|
||||
# create the menu server
|
||||
|
@ -1192,7 +1357,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.ubuntuMessagingMenuUnread(True)
|
||||
except Exception:
|
||||
withMessagingMenu = False
|
||||
print 'WARNING: messaging menu disabled'
|
||||
logger.warning('WARNING: messaging menu disabled')
|
||||
|
||||
# update the Ubuntu messaging menu
|
||||
def ubuntuMessagingMenuUpdate(self, drawAttention, newItem, toLabel):
|
||||
|
@ -1204,11 +1369,11 @@ class MyForm(QtGui.QMainWindow):
|
|||
|
||||
# has messageing menu been installed
|
||||
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
|
||||
|
||||
# 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
|
||||
else:
|
||||
self.newMessageItem = newItem
|
||||
|
@ -1298,7 +1463,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
stdout=subprocess.PIPE)
|
||||
gst_available=True
|
||||
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:
|
||||
try:
|
||||
subprocess.call(["mpg123", soundFilename],
|
||||
|
@ -1306,14 +1471,14 @@ class MyForm(QtGui.QMainWindow):
|
|||
stdout=subprocess.PIPE)
|
||||
gst_available=True
|
||||
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:
|
||||
try:
|
||||
subprocess.call(["aplay", soundFilename],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
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':
|
||||
# use winsound on Windows
|
||||
import winsound
|
||||
|
@ -1346,16 +1511,19 @@ class MyForm(QtGui.QMainWindow):
|
|||
else:
|
||||
self.tray.showMessage(title, subtitle, 1, 2000)
|
||||
|
||||
# set delete key in inbox
|
||||
def tableWidgetInboxKeyPressEvent(self, event):
|
||||
if event.key() == QtCore.Qt.Key_Delete:
|
||||
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):
|
||||
if event.key() == QtCore.Qt.Key_Delete:
|
||||
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):
|
||||
if 'darwin' in sys.platform or 'linux' in sys.platform:
|
||||
if shared.appdata == '':
|
||||
|
@ -1379,11 +1547,23 @@ class MyForm(QtGui.QMainWindow):
|
|||
if reply == QtGui.QMessageBox.Yes:
|
||||
shared.openKeysFile()
|
||||
|
||||
# menu button 'delete all treshed messages'
|
||||
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:
|
||||
return
|
||||
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):
|
||||
self.regenerateAddressesDialogInstance = regenerateAddressesDialog(
|
||||
self)
|
||||
|
@ -1409,6 +1589,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
), self.regenerateAddressesDialogInstance.ui.lineEditPassphrase.text().toUtf8(), self.regenerateAddressesDialogInstance.ui.checkBoxEighteenByteRipe.isChecked()))
|
||||
self.ui.tabWidget.setCurrentIndex(3)
|
||||
|
||||
# opens 'join chan' dialog
|
||||
def click_actionJoinChan(self):
|
||||
self.newChanDialogInstance = newChanDialog(self)
|
||||
if self.newChanDialogInstance.exec_():
|
||||
|
@ -1420,7 +1601,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
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()))
|
||||
addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get()
|
||||
print 'addressGeneratorReturnValue', addressGeneratorReturnValue
|
||||
logger.debug('addressGeneratorReturnValue ' + str(addressGeneratorReturnValue))
|
||||
if len(addressGeneratorReturnValue) == 0:
|
||||
QMessageBox.about(self, _translate("MainWindow", "Address already present"), _translate(
|
||||
"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.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()
|
||||
print 'addressGeneratorReturnValue', addressGeneratorReturnValue
|
||||
logger.debug('addressGeneratorReturnValue ' + str(addressGeneratorReturnValue))
|
||||
if addressGeneratorReturnValue == 'chan name does not match address':
|
||||
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."))
|
||||
|
@ -1458,6 +1639,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
QMessageBox.about(self, _translate("MainWindow", "Success"), _translate(
|
||||
"MainWindow", "Successfully joined chan. "))
|
||||
self.ui.tabWidget.setCurrentIndex(3)
|
||||
self.rerenderAddressBook()
|
||||
|
||||
def showConnectDialog(self):
|
||||
self.connectDialogInstance = connectDialog(self)
|
||||
|
@ -1468,11 +1650,18 @@ class MyForm(QtGui.QMainWindow):
|
|||
else:
|
||||
self.click_actionSettings()
|
||||
|
||||
def showMigrationWizard(self, level):
|
||||
self.migrationWizardInstance = Ui_MigrationWizard(["a"])
|
||||
if self.migrationWizardInstance.exec_():
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
def changeEvent(self, event):
|
||||
if event.type() == QtCore.QEvent.LanguageChange:
|
||||
self.ui.retranslateUi(self)
|
||||
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_subscriptions_popup_menu(False)
|
||||
self.init_sent_popup_menu(False)
|
||||
|
@ -1480,9 +1669,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
if event.type() == QtCore.QEvent.WindowStateChange:
|
||||
if self.windowState() & QtCore.Qt.WindowMinimized:
|
||||
if shared.config.getboolean('bitmessagesettings', 'minimizetotray') and not 'darwin' in sys.platform:
|
||||
self.appIndicatorHide()
|
||||
if 'win32' in sys.platform or 'win64' in sys.platform:
|
||||
self.setWindowFlags(Qt.ToolTip)
|
||||
QTimer.singleShot(0, self.appIndicatorHide)
|
||||
elif event.oldState() & QtCore.Qt.WindowMinimized:
|
||||
# The window state has just been changed to
|
||||
# Normal/Maximised/FullScreen
|
||||
|
@ -1496,14 +1683,17 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.appIndicatorShowOrHideWindow()
|
||||
|
||||
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(
|
||||
"MainWindow", "Processed %1 person-to-person messages.").arg(str(shared.numberOfMessagesProcessed)))
|
||||
|
||||
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(
|
||||
"MainWindow", "Processed %1 broadcast messages.").arg(str(shared.numberOfBroadcastsProcessed)))
|
||||
|
||||
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(
|
||||
"MainWindow", "Processed %1 public keys.").arg(str(shared.numberOfPubkeysProcessed)))
|
||||
|
||||
|
@ -1533,7 +1723,6 @@ class MyForm(QtGui.QMainWindow):
|
|||
shared.numberOfBytesSent = 0
|
||||
|
||||
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).
|
||||
streamNumberTotals = {}
|
||||
for host, streamNumber in shared.connectedHostsList.items():
|
||||
|
@ -1684,60 +1873,107 @@ class MyForm(QtGui.QMainWindow):
|
|||
def drawTrayIcon(self, iconFileName, inboxUnreadCount):
|
||||
self.tray.setIcon(self.calcTrayIcon(iconFileName, inboxUnreadCount))
|
||||
|
||||
def changedInboxUnread(self):
|
||||
def changedInboxUnread(self, row = None):
|
||||
self.drawTrayIcon(self.currentTrayIconFileName, self.findInboxUnreadCount())
|
||||
self.rerenderTabTreeMessages()
|
||||
self.rerenderTabTreeSubscriptions()
|
||||
self.rerenderTabTreeChans()
|
||||
|
||||
def findInboxUnreadCount(self):
|
||||
def findInboxUnreadCount(self, count = None):
|
||||
if count is None:
|
||||
queryreturn = sqlQuery('''SELECT count(*) from inbox WHERE folder='inbox' and read=0''')
|
||||
cnt = 0
|
||||
for row in queryreturn:
|
||||
cnt, = row
|
||||
return int(cnt)
|
||||
self.unreadCount = int(cnt)
|
||||
else:
|
||||
self.unreadCount = count
|
||||
return self.unreadCount
|
||||
|
||||
def updateSentItemStatusByToAddress(self, toAddress, textToDisplay):
|
||||
for i in range(self.ui.tableWidgetSent.rowCount()):
|
||||
rowAddress = str(self.ui.tableWidgetSent.item(
|
||||
for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]:
|
||||
treeWidget = self.widgetConvert(sent)
|
||||
if self.getCurrentFolder(treeWidget) != "sent":
|
||||
continue
|
||||
if treeWidget in [self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] and self.getCurrentAccount(treeWidget) != toAddress:
|
||||
continue
|
||||
|
||||
for i in range(sent.rowCount()):
|
||||
rowAddress = str(sent.item(
|
||||
i, 0).data(Qt.UserRole).toPyObject())
|
||||
if toAddress == rowAddress:
|
||||
self.ui.tableWidgetSent.item(i, 3).setToolTip(textToDisplay)
|
||||
sent.item(i, 3).setToolTip(textToDisplay)
|
||||
try:
|
||||
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:
|
||||
self.ui.tableWidgetSent.item(i, 3).setText(
|
||||
sent.item(i, 3).setText(
|
||||
textToDisplay[:newlinePosition])
|
||||
else:
|
||||
self.ui.tableWidgetSent.item(i, 3).setText(textToDisplay)
|
||||
sent.item(i, 3).setText(textToDisplay)
|
||||
|
||||
def updateSentItemStatusByAckdata(self, ackdata, textToDisplay):
|
||||
for i in range(self.ui.tableWidgetSent.rowCount()):
|
||||
toAddress = str(self.ui.tableWidgetSent.item(
|
||||
for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]:
|
||||
treeWidget = self.widgetConvert(sent)
|
||||
if self.getCurrentFolder(treeWidget) != "sent":
|
||||
continue
|
||||
for i in range(sent.rowCount()):
|
||||
toAddress = str(sent.item(
|
||||
i, 0).data(Qt.UserRole).toPyObject())
|
||||
tableAckdata = self.ui.tableWidgetSent.item(
|
||||
tableAckdata = sent.item(
|
||||
i, 3).data(Qt.UserRole).toPyObject()
|
||||
status, addressVersionNumber, streamNumber, ripe = decodeAddress(
|
||||
toAddress)
|
||||
if ackdata == tableAckdata:
|
||||
self.ui.tableWidgetSent.item(i, 3).setToolTip(textToDisplay)
|
||||
sent.item(i, 3).setToolTip(textToDisplay)
|
||||
try:
|
||||
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:
|
||||
self.ui.tableWidgetSent.item(i, 3).setText(
|
||||
sent.item(i, 3).setText(
|
||||
textToDisplay[:newlinePosition])
|
||||
else:
|
||||
self.ui.tableWidgetSent.item(i, 3).setText(textToDisplay)
|
||||
sent.item(i, 3).setText(textToDisplay)
|
||||
|
||||
def removeInboxRowByMsgid(self, msgid): # msgid and inventoryHash are the same thing
|
||||
for i in range(self.ui.tableWidgetInbox.rowCount()):
|
||||
if msgid == str(self.ui.tableWidgetInbox.item(i, 3).data(Qt.UserRole).toPyObject()):
|
||||
for inbox in ([
|
||||
self.ui.tableWidgetInbox,
|
||||
self.ui.tableWidgetInboxSubscriptions,
|
||||
self.ui.tableWidgetInboxChans]):
|
||||
for i in range(inbox.rowCount()):
|
||||
if msgid == str(inbox.item(i, 3).data(Qt.UserRole).toPyObject()):
|
||||
self.statusBar().showMessage(_translate(
|
||||
"MainWindow", "Message trashed"))
|
||||
self.ui.tableWidgetInbox.removeRow(i)
|
||||
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
|
||||
self.changedInboxUnread()
|
||||
|
||||
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):
|
||||
self.statusBar().showMessage(text)
|
||||
|
@ -1809,22 +2045,22 @@ class MyForm(QtGui.QMainWindow):
|
|||
i, 0).setTextColor(QApplication.palette().text().color())
|
||||
|
||||
def rerenderSentFromLabels(self):
|
||||
for i in range(self.ui.tableWidgetSent.rowCount()):
|
||||
fromAddress = str(self.ui.tableWidgetSent.item(
|
||||
for i in range(self.ui.tableWidgetInbox.rowCount()):
|
||||
fromAddress = str(self.ui.tableWidgetInbox.item(
|
||||
i, 1).data(Qt.UserRole).toPyObject())
|
||||
# Message might be from an address we own like a chan address. Let's look for that label.
|
||||
if shared.config.has_section(fromAddress):
|
||||
fromLabel = shared.config.get(fromAddress, 'label')
|
||||
else:
|
||||
fromLabel = fromAddress
|
||||
self.ui.tableWidgetSent.item(
|
||||
self.ui.tableWidgetInbox.item(
|
||||
i, 1).setText(unicode(fromLabel, 'utf-8'))
|
||||
self.ui.tableWidgetSent.item(
|
||||
self.ui.tableWidgetInbox.item(
|
||||
i, 1).setIcon(avatarize(fromAddress))
|
||||
|
||||
def rerenderSentToLabels(self):
|
||||
for i in range(self.ui.tableWidgetSent.rowCount()):
|
||||
addressToLookup = str(self.ui.tableWidgetSent.item(
|
||||
for i in range(self.ui.tableWidgetInbox.rowCount()):
|
||||
addressToLookup = str(self.ui.tableWidgetInbox.item(
|
||||
i, 0).data(Qt.UserRole).toPyObject())
|
||||
toLabel = ''
|
||||
queryreturn = sqlQuery(
|
||||
|
@ -1839,40 +2075,46 @@ class MyForm(QtGui.QMainWindow):
|
|||
toLabel = shared.config.get(addressToLookup, 'label')
|
||||
if toLabel == '':
|
||||
toLabel = addressToLookup
|
||||
self.ui.tableWidgetSent.item(
|
||||
self.ui.tableWidgetInbox.item(
|
||||
i, 0).setText(unicode(toLabel, 'utf-8'))
|
||||
|
||||
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)
|
||||
|
||||
# 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')
|
||||
for row in queryreturn:
|
||||
label, address = row
|
||||
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)
|
||||
addRow(address, label, AccountMixin.NORMAL)
|
||||
|
||||
# sort
|
||||
self.ui.tableWidgetAddressBook.sortItems(0, QtCore.Qt.AscendingOrder)
|
||||
self.ui.tableWidgetAddressBook.setSortingEnabled(True)
|
||||
|
||||
def rerenderSubscriptions(self):
|
||||
self.ui.tableWidgetSubscriptions.setRowCount(0)
|
||||
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)
|
||||
self.rerenderTabTreeSubscriptions()
|
||||
|
||||
def rerenderBlackWhiteList(self):
|
||||
self.ui.tableWidgetBlacklist.setRowCount(0)
|
||||
|
@ -1898,18 +2140,33 @@ class MyForm(QtGui.QMainWindow):
|
|||
|
||||
def click_pushButtonTTL(self):
|
||||
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. \
|
||||
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 \
|
||||
more work your computer must do to send the message. A Time-To-Live of four or five days is often appropriate."), QMessageBox.Ok)
|
||||
"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
|
||||
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)
|
||||
|
||||
def click_pushButtonSend(self):
|
||||
self.statusBar().showMessage('')
|
||||
|
||||
if self.ui.tabWidgetSend.currentIndex() == 0:
|
||||
# message to specific people
|
||||
sendMessageToPeople = True
|
||||
fromAddress = str(self.ui.comboBoxSendFrom.itemData(
|
||||
self.ui.comboBoxSendFrom.currentIndex(),
|
||||
Qt.UserRole).toString())
|
||||
toAddresses = str(self.ui.lineEditTo.text())
|
||||
fromAddress = str(self.ui.labelFrom.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
|
||||
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(
|
||||
"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
|
||||
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()
|
||||
for s in toAddresses.replace(',', ';').split(';')]
|
||||
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.
|
||||
for toAddress in toAddressesList:
|
||||
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(
|
||||
toAddress)
|
||||
if status != 'success':
|
||||
with shared.printLock:
|
||||
print 'Error: Could not decode', toAddress, ':', status
|
||||
logger.error('Error: Could not decode ' + toAddress + ':' + status)
|
||||
|
||||
if status == 'missingbm':
|
||||
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."))
|
||||
else:
|
||||
toAddress = addBMIfNotPresent(toAddress)
|
||||
|
||||
if addressVersionNumber > 4 or addressVersionNumber <= 1:
|
||||
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)))
|
||||
|
@ -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))
|
||||
|
||||
self.ui.comboBoxSendFrom.setCurrentIndex(0)
|
||||
self.ui.labelFrom.setText('')
|
||||
self.ui.lineEditTo.setText('')
|
||||
self.ui.lineEditSubject.setText('')
|
||||
self.ui.textEditMessage.setText('')
|
||||
self.ui.tabWidget.setCurrentIndex(2)
|
||||
self.ui.tableWidgetSent.setCurrentCell(0, 0)
|
||||
if self.replyFromTab is not None:
|
||||
self.ui.tabWidget.setCurrentIndex(self.replyFromTab)
|
||||
self.replyFromTab = None
|
||||
self.statusBar().showMessage(_translate(
|
||||
"MainWindow", "Message queued."))
|
||||
#self.ui.tableWidgetInbox.setCurrentCell(0, 0)
|
||||
else:
|
||||
self.statusBar().showMessage(_translate(
|
||||
"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
|
||||
# user interface when the POW is done generating.
|
||||
ackdata = OpenSSL.rand(32)
|
||||
toAddress = self.str_broadcast_subscribers
|
||||
toAddress = str_broadcast_subscribers
|
||||
ripe = ''
|
||||
t = ('', # msgid. We don't know what this will be until the POW is done.
|
||||
toAddress,
|
||||
|
@ -2048,20 +2329,20 @@ more work your computer must do to send the message. A Time-To-Live of four or f
|
|||
sqlExecute(
|
||||
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', *t)
|
||||
|
||||
toLabel = self.str_broadcast_subscribers
|
||||
toLabel = str_broadcast_subscribers
|
||||
|
||||
self.displayNewSentMessage(
|
||||
toAddress, toLabel, fromAddress, subject, message, ackdata)
|
||||
|
||||
shared.workerQueue.put(('sendbroadcast', ''))
|
||||
|
||||
self.ui.comboBoxSendFrom.setCurrentIndex(0)
|
||||
self.ui.labelFrom.setText('')
|
||||
self.ui.lineEditTo.setText('')
|
||||
self.ui.lineEditSubject.setText('')
|
||||
self.ui.textEditMessage.setText('')
|
||||
self.ui.tabWidget.setCurrentIndex(2)
|
||||
self.ui.tableWidgetSent.setCurrentCell(0, 0)
|
||||
self.ui.comboBoxSendFromBroadcast.setCurrentIndex(0)
|
||||
self.ui.lineEditSubjectBroadcast.setText('')
|
||||
self.ui.textEditMessageBroadcast.setText('')
|
||||
self.ui.tabWidget.setCurrentIndex(1)
|
||||
self.ui.tableWidgetInboxSubscriptions.setCurrentCell(0, 0)
|
||||
self.statusBar().showMessage(_translate(
|
||||
"MainWindow", "Broadcast queued."))
|
||||
|
||||
def click_pushButtonLoadFromAddressBook(self):
|
||||
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(
|
||||
"MainWindow", "Fetched address from namecoin identity."))
|
||||
|
||||
def redrawLabelFrom(self, index):
|
||||
self.ui.labelFrom.setText(
|
||||
self.ui.comboBoxSendFrom.itemData(index).toPyObject())
|
||||
self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(self.ui.comboBoxSendFrom.itemData(index).toPyObject())
|
||||
|
||||
def setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(self, address):
|
||||
def setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(self, address):
|
||||
# If this is a chan then don't let people broadcast because no one
|
||||
# should subscribe to chan addresses.
|
||||
if shared.safeConfigGetBoolean(str(address), 'chan'):
|
||||
self.ui.radioButtonSpecific.click()
|
||||
self.ui.radioButtonBroadcast.setEnabled(False)
|
||||
if shared.safeConfigGetBoolean(str(address), 'mailinglist'):
|
||||
self.ui.tabWidgetSend.setCurrentIndex(1)
|
||||
else:
|
||||
self.ui.radioButtonBroadcast.setEnabled(True)
|
||||
self.ui.tabWidgetSend.setCurrentIndex(0)
|
||||
|
||||
def rerenderComboBoxSendFrom(self):
|
||||
self.ui.comboBoxSendFrom.clear()
|
||||
self.ui.labelFrom.setText('')
|
||||
configSections = shared.config.sections()
|
||||
for addressInKeysFile in configSections:
|
||||
if addressInKeysFile != 'bitmessagesettings':
|
||||
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.
|
||||
if isEnabled:
|
||||
self.ui.comboBoxSendFrom.insertItem(0, avatarize(addressInKeysFile), unicode(shared.config.get(
|
||||
isMaillinglist = shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist')
|
||||
if isEnabled and not isMaillinglist:
|
||||
self.ui.comboBoxSendFrom.addItem(avatarize(addressInKeysFile), unicode(shared.config.get(
|
||||
addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile)
|
||||
# self.ui.comboBoxSendFrom.model().sort(1, Qt.AscendingOrder)
|
||||
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, '', '')
|
||||
if(self.ui.comboBoxSendFrom.count() == 2):
|
||||
self.ui.comboBoxSendFrom.setCurrentIndex(1)
|
||||
self.redrawLabelFrom(self.ui.comboBoxSendFrom.currentIndex())
|
||||
else:
|
||||
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
|
||||
# receives a message to an address that is acting as a
|
||||
# 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):
|
||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
||||
try:
|
||||
fromLabel = shared.config.get(fromAddress, 'label')
|
||||
except:
|
||||
fromLabel = ''
|
||||
if fromLabel == '':
|
||||
fromLabel = fromAddress
|
||||
acct = accountClass(fromAddress)
|
||||
acct.parseMessage(toAddress, fromAddress, subject, message)
|
||||
for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]:
|
||||
treeWidget = self.widgetConvert(sent)
|
||||
if self.getCurrentFolder(treeWidget) != "sent":
|
||||
continue
|
||||
if treeWidget == self.ui.treeWidgetYourIdentities and self.getCurrentAccount(treeWidget) != fromAddress:
|
||||
continue
|
||||
elif treeWidget in [self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] and self.getCurrentAccount(treeWidget) != toAddress:
|
||||
continue
|
||||
|
||||
self.ui.tableWidgetSent.setSortingEnabled(False)
|
||||
self.ui.tableWidgetSent.insertRow(0)
|
||||
if toLabel == '':
|
||||
newItem = QtGui.QTableWidgetItem(unicode(toAddress, 'utf-8'))
|
||||
newItem.setToolTip(unicode(toAddress, 'utf-8'))
|
||||
else:
|
||||
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)
|
||||
self.addMessageListItemSent(sent, toAddress, fromAddress, subject, "msgqueued", ackdata, time.time())
|
||||
self.getAccountTextedit(acct).setPlainText(unicode(message, 'utf-8)'))
|
||||
|
||||
def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message):
|
||||
if toAddress == str_broadcast_subscribers:
|
||||
acct = accountClass(fromAddress)
|
||||
else:
|
||||
acct = accountClass(toAddress)
|
||||
inbox = self.getAccountMessagelist(acct)
|
||||
ret = None
|
||||
for treeWidget in [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans]:
|
||||
tableWidget = self.widgetConvert(treeWidget)
|
||||
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)
|
||||
elif treeWidget == self.ui.treeWidgetYourIdentities and self.getCurrentAccount(treeWidget) is None:
|
||||
ret = self.addMessageListItemInbox(tableWidget, "inbox", inventoryHash, toAddress, fromAddress, subject, time.time(), 0)
|
||||
if ret is None:
|
||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||
fromLabel = ''
|
||||
queryreturn = sqlQuery(
|
||||
'''select label from addressbook where address=?''', fromAddress)
|
||||
if queryreturn != []:
|
||||
for row in queryreturn:
|
||||
fromLabel, = row
|
||||
acct.parseMessage(toAddress, fromAddress, subject, "")
|
||||
else:
|
||||
# There might be a label in the subscriptions table
|
||||
queryreturn = sqlQuery(
|
||||
'''select label from subscriptions where address=?''', fromAddress)
|
||||
if queryreturn != []:
|
||||
for row in queryreturn:
|
||||
fromLabel, = row
|
||||
|
||||
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
|
||||
|
||||
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'))
|
||||
acct = ret
|
||||
self.propagateUnreadCount(acct.address)
|
||||
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:
|
||||
newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8'))
|
||||
newItem.setToolTip(unicode(unicode(fromLabel, '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(fromLabel, 'utf-8'), self.SOUND_KNOWN, unicode(fromLabel, 'utf-8'))
|
||||
newItem.setData(Qt.UserRole, str(fromAddress))
|
||||
newItem.setFont(font)
|
||||
newItem.setIcon(avatarize(fromAddress))
|
||||
self.ui.tableWidgetInbox.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.
|
||||
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)
|
||||
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)
|
||||
if self.getCurrentAccount() is not None and ((self.getCurrentFolder(treeWidget) != "inbox" and self.getCurrentFolder(treeWidget) is not None) or self.getCurrentAccount(treeWidget) != acct.address):
|
||||
# Ubuntu should notify of new message irespective of whether it's in current message list or not
|
||||
self.ubuntuMessagingMenuUpdate(True, None, acct.toLabel)
|
||||
return
|
||||
|
||||
def click_pushButtonAddAddressBook(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):
|
||||
queryreturn = sqlQuery('''select * from addressbook where address=?''', address)
|
||||
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)
|
||||
self.rerenderAddressBook()
|
||||
self.rerenderInboxFromLabels()
|
||||
self.rerenderSentToLabels()
|
||||
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.
|
||||
if shared.isAddressInMySubscriptionsList(address):
|
||||
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)
|
||||
sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',str(label),address,True)
|
||||
self.rerenderInboxFromLabels()
|
||||
shared.reloadBroadcastSendersForWhichImWatching()
|
||||
self.rerenderAddressBook()
|
||||
self.rerenderTabTreeSubscriptions()
|
||||
|
||||
def click_pushButtonAddSubscription(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()))
|
||||
# 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):
|
||||
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
|
||||
label = self.NewSubscriptionDialogInstance.ui.newsubscriptionlabel.text().toUtf8()
|
||||
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))
|
||||
|
||||
def click_pushButtonStatusIcon(self):
|
||||
print 'click_pushButtonStatusIcon'
|
||||
logger.debug('click_pushButtonStatusIcon')
|
||||
self.iconGlossaryInstance = iconGlossaryDialog(self)
|
||||
if self.iconGlossaryInstance.exec_():
|
||||
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."))
|
||||
shared.config.set('bitmessagesettings', 'port', str(
|
||||
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())[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':
|
||||
|
@ -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(
|
||||
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
|
||||
|
||||
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)
|
||||
# For Modal dialogs
|
||||
if self.dialog.exec_():
|
||||
currentRow = self.ui.tableWidgetYourIdentities.currentRow()
|
||||
addressAtCurrentRow = str(
|
||||
self.ui.tableWidgetYourIdentities.item(currentRow, 1).text())
|
||||
addressAtCurrentRow = self.getCurrentAccount()
|
||||
if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'):
|
||||
return
|
||||
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')
|
||||
# Set the color to either black or grey
|
||||
if shared.config.getboolean(addressAtCurrentRow, 'enabled'):
|
||||
self.ui.tableWidgetYourIdentities.item(
|
||||
currentRow, 1).setTextColor(QApplication.palette()
|
||||
self.setCurrentItemColor(QApplication.palette()
|
||||
.text().color())
|
||||
else:
|
||||
self.ui.tableWidgetYourIdentities.item(
|
||||
currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128))
|
||||
self.setCurrentItemColor(QtGui.QColor(128, 128, 128))
|
||||
else:
|
||||
shared.config.set(str(
|
||||
addressAtCurrentRow), 'mailinglist', 'true')
|
||||
shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str(
|
||||
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()
|
||||
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):
|
||||
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)
|
||||
# For Modal dialogs
|
||||
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(
|
||||
), self.dialog.ui.lineEditPassphrase.text().toUtf8(), self.dialog.ui.checkBoxEighteenByteRipe.isChecked()))
|
||||
else:
|
||||
print 'new address dialog box rejected'
|
||||
logger.debug('new address dialog box rejected')
|
||||
|
||||
# Quit selected from menu or application indicator
|
||||
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:
|
||||
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()
|
||||
self.tray.hide()
|
||||
# unregister the messaging system
|
||||
if self.mmapp is not None:
|
||||
self.mmapp.unregister()
|
||||
|
||||
# settings = QSettings("Bitmessage", "PyBitmessage")
|
||||
# settings.setValue("geometry", self.saveGeometry())
|
||||
# settings.setValue("state", self.saveState())
|
||||
|
||||
self.statusBar().showMessage(_translate(
|
||||
"MainWindow", "All done. Closing user interface..."))
|
||||
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()
|
||||
|
||||
def on_action_InboxMessageForceHtml(self):
|
||||
currentInboxRow = self.ui.tableWidgetInbox.currentRow()
|
||||
|
||||
msgid = str(self.ui.tableWidgetInbox.item(
|
||||
currentInboxRow, 3).data(Qt.UserRole).toPyObject())
|
||||
msgid = self.getCurrentMessageId()
|
||||
textEdit = self.getCurrentMessageTextedit()
|
||||
if not msgid:
|
||||
return
|
||||
queryreturn = sqlQuery(
|
||||
'''select message from inbox where msgid=?''', msgid)
|
||||
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 = shared.fixPotentiallyInvalidUTF8Data(content)
|
||||
content = unicode(content, 'utf-8)')
|
||||
self.ui.textEditInboxMessage.setHtml(QtCore.QString(content))
|
||||
textEdit.setHtml(QtCore.QString(content))
|
||||
|
||||
def on_action_InboxMarkUnread(self):
|
||||
tableWidget = self.getCurrentMessagelist()
|
||||
if not tableWidget:
|
||||
return
|
||||
font = QFont()
|
||||
font.setBold(True)
|
||||
inventoryHashesToMarkUnread = []
|
||||
for row in self.ui.tableWidgetInbox.selectedIndexes():
|
||||
modified = 0
|
||||
for row in tableWidget.selectedIndexes():
|
||||
currentRow = row.row()
|
||||
inventoryHashToMarkUnread = str(self.ui.tableWidgetInbox.item(
|
||||
inventoryHashToMarkUnread = str(tableWidget.item(
|
||||
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)
|
||||
self.ui.tableWidgetInbox.item(currentRow, 0).setFont(font)
|
||||
self.ui.tableWidgetInbox.item(currentRow, 1).setFont(font)
|
||||
self.ui.tableWidgetInbox.item(currentRow, 2).setFont(font)
|
||||
self.ui.tableWidgetInbox.item(currentRow, 3).setFont(font)
|
||||
tableWidget.item(currentRow, 0).setFont(font)
|
||||
tableWidget.item(currentRow, 1).setFont(font)
|
||||
tableWidget.item(currentRow, 2).setFont(font)
|
||||
tableWidget.item(currentRow, 3).setFont(font)
|
||||
#sqlite requires the exact number of ?s to prevent injection
|
||||
sqlExecute('''UPDATE inbox SET read=0 WHERE msgid IN (%s)''' % (
|
||||
"?," * len(inventoryHashesToMarkUnread))[:-1], *inventoryHashesToMarkUnread)
|
||||
self.changedInboxUnread()
|
||||
# self.ui.tableWidgetInbox.selectRow(currentRow + 1)
|
||||
if modified == 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.
|
||||
# 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.
|
||||
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'
|
||||
|
||||
def on_action_InboxReply(self):
|
||||
currentInboxRow = self.ui.tableWidgetInbox.currentRow()
|
||||
toAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item(
|
||||
tableWidget = self.getCurrentMessagelist()
|
||||
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())
|
||||
fromAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item(
|
||||
acct = accountClass(toAddressAtCurrentInboxRow)
|
||||
fromAddressAtCurrentInboxRow = str(tableWidget.item(
|
||||
currentInboxRow, 1).data(Qt.UserRole).toPyObject())
|
||||
msgid = str(self.ui.tableWidgetInbox.item(
|
||||
msgid = str(tableWidget.item(
|
||||
currentInboxRow, 3).data(Qt.UserRole).toPyObject())
|
||||
queryreturn = sqlQuery(
|
||||
'''select message from inbox where msgid=?''', msgid)
|
||||
if queryreturn != []:
|
||||
for row in queryreturn:
|
||||
messageAtCurrentInboxRow, = row
|
||||
if toAddressAtCurrentInboxRow == self.str_broadcast_subscribers:
|
||||
self.ui.labelFrom.setText('')
|
||||
acct.parseMessage(toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow, unicode(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toPyObject(), 'utf-8'), messageAtCurrentInboxRow)
|
||||
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):
|
||||
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)
|
||||
self.ui.labelFrom.setText('')
|
||||
elif not shared.config.getboolean(toAddressAtCurrentInboxRow, 'enabled'):
|
||||
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)
|
||||
self.ui.labelFrom.setText('')
|
||||
else:
|
||||
self.ui.labelFrom.setText(toAddressAtCurrentInboxRow)
|
||||
self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(toAddressAtCurrentInboxRow)
|
||||
self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(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 shared.config.has_section(toAddressAtCurrentInboxRow):
|
||||
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))
|
||||
|
||||
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:
|
||||
currentIndex = listOfAddressesInComboBoxSendFrom.index(toAddressAtCurrentInboxRow)
|
||||
self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex)
|
||||
widget['from'].setCurrentIndex(currentIndex)
|
||||
else:
|
||||
self.ui.comboBoxSendFrom.setCurrentIndex(0)
|
||||
widget['from'].setCurrentIndex(0)
|
||||
|
||||
quotedText = self.quoted_text(unicode(messageAtCurrentInboxRow, 'utf-8'))
|
||||
self.ui.textEditMessage.setText(quotedText)
|
||||
if self.ui.tableWidgetInbox.item(currentInboxRow, 2).text()[0:3] in ['Re:', 'RE:']:
|
||||
self.ui.lineEditSubject.setText(
|
||||
self.ui.tableWidgetInbox.item(currentInboxRow, 2).text())
|
||||
widget['message'].setText(quotedText)
|
||||
if acct.subject[0:3] in ['Re:', 'RE:']:
|
||||
widget['subject'].setText(acct.subject)
|
||||
else:
|
||||
self.ui.lineEditSubject.setText(
|
||||
'Re: ' + self.ui.tableWidgetInbox.item(currentInboxRow, 2).text())
|
||||
self.ui.radioButtonSpecific.setChecked(True)
|
||||
widget['subject'].setText('Re: ' + acct.subject)
|
||||
self.ui.tabWidget.setCurrentIndex(1)
|
||||
|
||||
def on_action_InboxAddSenderToAddressBook(self):
|
||||
currentInboxRow = self.ui.tableWidgetInbox.currentRow()
|
||||
# self.ui.tableWidgetInbox.item(currentRow,1).data(Qt.UserRole).toPyObject()
|
||||
addressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item(
|
||||
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 addressbook where address=?''',
|
||||
addressAtCurrentInboxRow)
|
||||
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 (?,?)''',
|
||||
'--New entry. Change label in Address Book.--',
|
||||
addressAtCurrentInboxRow)
|
||||
self.ui.tabWidget.setCurrentIndex(5)
|
||||
self.ui.tableWidgetAddressBook.setCurrentCell(0, 0)
|
||||
self.rerenderAddressBook()
|
||||
self.statusBar().showMessage(_translate(
|
||||
"MainWindow", "Entry added to the Address Book. Edit the label to your liking."))
|
||||
else:
|
||||
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."))
|
||||
|
||||
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
|
||||
def on_action_InboxTrash(self):
|
||||
while self.ui.tableWidgetInbox.selectedIndexes() != []:
|
||||
currentRow = self.ui.tableWidgetInbox.selectedIndexes()[0].row()
|
||||
inventoryHashToTrash = str(self.ui.tableWidgetInbox.item(
|
||||
tableWidget = self.getCurrentMessagelist()
|
||||
if not tableWidget:
|
||||
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())
|
||||
sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', inventoryHashToTrash)
|
||||
self.ui.textEditInboxMessage.setText("")
|
||||
self.ui.tableWidgetInbox.removeRow(currentRow)
|
||||
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."))
|
||||
if currentRow == 0:
|
||||
self.ui.tableWidgetInbox.selectRow(currentRow)
|
||||
if folder == "trash" or shifted:
|
||||
sqlExecute('''DELETE FROM inbox WHERE msgid=?''', inventoryHashToTrash)
|
||||
else:
|
||||
self.ui.tableWidgetInbox.selectRow(currentRow - 1)
|
||||
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(
|
||||
"MainWindow", "Moved items to trash."))
|
||||
if currentRow == 0:
|
||||
tableWidget.selectRow(currentRow)
|
||||
else:
|
||||
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):
|
||||
currentInboxRow = self.ui.tableWidgetInbox.currentRow()
|
||||
tableWidget = self.getCurrentMessagelist()
|
||||
if not tableWidget:
|
||||
return
|
||||
currentInboxRow = tableWidget.currentRow()
|
||||
try:
|
||||
subjectAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item(currentInboxRow,2).text())
|
||||
subjectAtCurrentInboxRow = str(tableWidget.item(currentInboxRow,2).text())
|
||||
except:
|
||||
subjectAtCurrentInboxRow = ''
|
||||
|
||||
# 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())
|
||||
queryreturn = sqlQuery(
|
||||
'''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.close()
|
||||
except Exception, e:
|
||||
sys.stderr.write('Write error: '+ e)
|
||||
logger.exception('Message not saved', exc_info=True)
|
||||
self.statusBar().showMessage(_translate("MainWindow", "Write error."))
|
||||
|
||||
# Send item on the Sent tab to trash
|
||||
def on_action_SentTrash(self):
|
||||
while self.ui.tableWidgetSent.selectedIndexes() != []:
|
||||
currentRow = self.ui.tableWidgetSent.selectedIndexes()[0].row()
|
||||
ackdataToTrash = str(self.ui.tableWidgetSent.item(
|
||||
currentRow = 0
|
||||
unread = False
|
||||
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())
|
||||
sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdataToTrash)
|
||||
self.ui.textEditSentMessage.setPlainText("")
|
||||
self.ui.tableWidgetSent.removeRow(currentRow)
|
||||
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."))
|
||||
if currentRow == 0:
|
||||
self.ui.tableWidgetSent.selectRow(currentRow)
|
||||
if folder == "trash" or shifted:
|
||||
sqlExecute('''DELETE FROM sent WHERE ackdata=?''', ackdataToTrash)
|
||||
else:
|
||||
self.ui.tableWidgetSent.selectRow(currentRow - 1)
|
||||
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(
|
||||
"MainWindow", "Moved items to trash."))
|
||||
if currentRow == 0:
|
||||
self.ui.tableWidgetInbox.selectRow(currentRow)
|
||||
else:
|
||||
self.ui.tableWidgetInbox.selectRow(currentRow - 1)
|
||||
|
||||
def on_action_ForceSend(self):
|
||||
currentRow = self.ui.tableWidgetSent.currentRow()
|
||||
addressAtCurrentRow = str(self.ui.tableWidgetSent.item(
|
||||
currentRow = self.ui.tableWidgetInbox.currentRow()
|
||||
addressAtCurrentRow = str(self.ui.tableWidgetInbox.item(
|
||||
currentRow, 0).data(Qt.UserRole).toPyObject())
|
||||
toRipe = decodeAddress(addressAtCurrentRow)[3]
|
||||
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', ''))
|
||||
|
||||
def on_action_SentClipboard(self):
|
||||
currentRow = self.ui.tableWidgetSent.currentRow()
|
||||
addressAtCurrentRow = str(self.ui.tableWidgetSent.item(
|
||||
currentRow = self.ui.tableWidgetInbox.currentRow()
|
||||
addressAtCurrentRow = str(self.ui.tableWidgetInbox.item(
|
||||
currentRow, 0).data(Qt.UserRole).toPyObject())
|
||||
clipboard = QtGui.QApplication.clipboard()
|
||||
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())
|
||||
# Then subscribe to it... provided it's not already in the address book
|
||||
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
|
||||
labelAtCurrentRow = self.ui.tableWidgetAddressBook.item(currentRow,0).text().toUtf8()
|
||||
self.addSubscription(addressAtCurrentRow, labelAtCurrentRow)
|
||||
self.ui.tabWidget.setCurrentIndex(4)
|
||||
|
||||
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.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()
|
||||
|
||||
def on_action_SubscriptionsDelete(self):
|
||||
print 'clicked Delete'
|
||||
currentRow = self.ui.tableWidgetSubscriptions.currentRow()
|
||||
labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 0).text().toUtf8()
|
||||
addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 1).text()
|
||||
sqlExecute('''DELETE FROM subscriptions WHERE label=? AND address=?''',
|
||||
str(labelAtCurrentRow), str(addressAtCurrentRow))
|
||||
self.ui.tableWidgetSubscriptions.removeRow(currentRow)
|
||||
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:
|
||||
return
|
||||
address = self.getCurrentAccount()
|
||||
sqlExecute('''DELETE FROM subscriptions WHERE address=?''',
|
||||
address)
|
||||
self.rerenderTabTreeSubscriptions()
|
||||
self.rerenderInboxFromLabels()
|
||||
self.rerenderAddressBook()
|
||||
shared.reloadBroadcastSendersForWhichImWatching()
|
||||
|
||||
def on_action_SubscriptionsClipboard(self):
|
||||
currentRow = self.ui.tableWidgetSubscriptions.currentRow()
|
||||
addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 1).text()
|
||||
address = self.getCurrentAccount()
|
||||
clipboard = QtGui.QApplication.clipboard()
|
||||
clipboard.setText(str(addressAtCurrentRow))
|
||||
clipboard.setText(str(address))
|
||||
|
||||
def on_action_SubscriptionsEnable(self):
|
||||
currentRow = self.ui.tableWidgetSubscriptions.currentRow()
|
||||
labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 0).text().toUtf8()
|
||||
addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 1).text()
|
||||
address = self.getCurrentAccount()
|
||||
sqlExecute(
|
||||
'''update subscriptions set enabled=1 WHERE label=? AND address=?''',
|
||||
str(labelAtCurrentRow), str(addressAtCurrentRow))
|
||||
self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 0).setTextColor(QApplication.palette().text().color())
|
||||
self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 1).setTextColor(QApplication.palette().text().color())
|
||||
'''update subscriptions set enabled=1 WHERE address=?''',
|
||||
address)
|
||||
account = self.getCurrentItem()
|
||||
account.setEnabled(True)
|
||||
self.rerenderAddressBook()
|
||||
shared.reloadBroadcastSendersForWhichImWatching()
|
||||
|
||||
def on_action_SubscriptionsDisable(self):
|
||||
currentRow = self.ui.tableWidgetSubscriptions.currentRow()
|
||||
labelAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 0).text().toUtf8()
|
||||
addressAtCurrentRow = self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 1).text()
|
||||
address = self.getCurrentAccount()
|
||||
sqlExecute(
|
||||
'''update subscriptions set enabled=0 WHERE label=? AND address=?''',
|
||||
str(labelAtCurrentRow), str(addressAtCurrentRow))
|
||||
self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128))
|
||||
self.ui.tableWidgetSubscriptions.item(
|
||||
currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128))
|
||||
'''update subscriptions set enabled=0 WHERE address=?''',
|
||||
address)
|
||||
account = self.getCurrentItem()
|
||||
account.setEnabled(False)
|
||||
self.rerenderAddressBook()
|
||||
shared.reloadBroadcastSendersForWhichImWatching()
|
||||
|
||||
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.ui.tableWidgetSubscriptions.mapToGlobal(point))
|
||||
self.ui.treeWidgetSubscriptions.mapToGlobal(point))
|
||||
|
||||
# Group of functions for the Blacklist dialog box
|
||||
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(
|
||||
'''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
|
||||
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):
|
||||
self.click_NewAddressDialog()
|
||||
|
||||
def on_action_YourIdentitiesEnable(self):
|
||||
currentRow = self.ui.tableWidgetYourIdentities.currentRow()
|
||||
addressAtCurrentRow = str(
|
||||
self.ui.tableWidgetYourIdentities.item(currentRow, 1).text())
|
||||
shared.config.set(addressAtCurrentRow, 'enabled', 'true')
|
||||
shared.writeKeysFile()
|
||||
self.ui.tableWidgetYourIdentities.item(
|
||||
currentRow, 0).setTextColor(QApplication.palette().text().color())
|
||||
self.ui.tableWidgetYourIdentities.item(
|
||||
currentRow, 1).setTextColor(QApplication.palette().text().color())
|
||||
self.ui.tableWidgetYourIdentities.item(
|
||||
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
|
||||
def on_action_YourIdentitiesDelete(self):
|
||||
account = self.getCurrentItem()
|
||||
if account.type == AccountMixin.NORMAL:
|
||||
return # maybe in the future
|
||||
elif account.type == AccountMixin.CHAN:
|
||||
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:
|
||||
shared.config.remove_section(str(account.address))
|
||||
else:
|
||||
return
|
||||
else:
|
||||
return
|
||||
shared.writeKeysFile()
|
||||
shared.reloadMyAddressHashes()
|
||||
self.rerenderAddressBook()
|
||||
if account.type == AccountMixin.NORMAL:
|
||||
self.rerenderTabTreeMessages()
|
||||
elif account.type == AccountMixin.CHAN:
|
||||
self.rerenderTabTreeChans()
|
||||
|
||||
def on_action_YourIdentitiesClipboard(self):
|
||||
currentRow = self.ui.tableWidgetYourIdentities.currentRow()
|
||||
addressAtCurrentRow = self.ui.tableWidgetYourIdentities.item(
|
||||
currentRow, 1).text()
|
||||
def on_action_Enable(self):
|
||||
addressAtCurrentRow = self.getCurrentAccount()
|
||||
self.enableIdentity(addressAtCurrentRow)
|
||||
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.setText(str(addressAtCurrentRow))
|
||||
clipboard.setText(str(address))
|
||||
|
||||
def on_action_YourIdentitiesSetAvatar(self):
|
||||
self.on_action_SetAvatar(self.ui.tableWidgetYourIdentities)
|
||||
#set avatar functions
|
||||
def on_action_TreeWidgetSetAvatar(self):
|
||||
address = self.getCurrentAccount()
|
||||
self.setAvatar(address)
|
||||
|
||||
def on_action_AddressBookSetAvatar(self):
|
||||
self.on_action_SetAvatar(self.ui.tableWidgetAddressBook)
|
||||
|
||||
def on_action_SubscriptionsSetAvatar(self):
|
||||
self.on_action_SetAvatar(self.ui.tableWidgetSubscriptions)
|
||||
|
||||
def on_action_BlacklistSetAvatar(self):
|
||||
self.on_action_SetAvatar(self.ui.tableWidgetBlacklist)
|
||||
|
||||
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()
|
||||
addressAtCurrentRow = thisTableWidget.item(
|
||||
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()
|
||||
extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA']
|
||||
# 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 != '':
|
||||
copied = QtCore.QFile.copy(sourcefile, destination)
|
||||
if not copied:
|
||||
print 'couldn\'t copy :('
|
||||
return False
|
||||
logger.error('couldn\'t copy :(')
|
||||
# set the icon
|
||||
thisTableWidget.item(
|
||||
currentRow, 0).setIcon(avatarize(addressAtCurrentRow))
|
||||
self.rerenderSubscriptions()
|
||||
self.rerenderTabTreeMessages()
|
||||
self.rerenderTabTreeSubscriptions()
|
||||
self.rerenderTabTreeChans()
|
||||
self.rerenderComboBoxSendFrom()
|
||||
self.rerenderComboBoxSendFromBroadcast()
|
||||
self.rerenderInboxFromLabels()
|
||||
self.rerenderInboxToLabels()
|
||||
self.rerenderSentFromLabels()
|
||||
self.rerenderSentToLabels()
|
||||
self.rerenderBlackWhiteList()
|
||||
# generate identicon
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
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.ui.tableWidgetYourIdentities.mapToGlobal(point))
|
||||
self.ui.treeWidgetChans.mapToGlobal(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):
|
||||
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
|
||||
# menu option (Force Send) if it is.
|
||||
currentRow = self.ui.tableWidgetSent.currentRow()
|
||||
ackData = str(self.ui.tableWidgetSent.item(
|
||||
currentRow = self.ui.tableWidgetInbox.currentRow()
|
||||
if currentRow >= 0:
|
||||
ackData = str(self.ui.tableWidgetInbox.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))
|
||||
|
||||
self.popMenuSent.exec_(self.ui.tableWidgetInbox.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)
|
||||
searchLine = self.getCurrentSearchLine()
|
||||
searchOption = self.getCurrentSearchOption()
|
||||
if searchLine:
|
||||
searchKeyword = searchLine.text().toUtf8().data()
|
||||
searchLine.setText(QString(""))
|
||||
messageTextedit = self.getCurrentMessageTextedit()
|
||||
if messageTextedit:
|
||||
messageTextedit.setPlainText(QString(""))
|
||||
messagelist = self.getCurrentMessagelist()
|
||||
if messagelist:
|
||||
account = self.getCurrentAccount()
|
||||
folder = self.getCurrentFolder()
|
||||
self.loadMessagelist(messagelist, account, folder, 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 treeWidgetItemClicked(self):
|
||||
messagelist = self.getCurrentMessagelist()
|
||||
if messagelist:
|
||||
account = self.getCurrentAccount()
|
||||
folder = self.getCurrentFolder()
|
||||
if folder == "new":
|
||||
self.loadMessagelist(messagelist, account, None, unreadOnly = True)
|
||||
else:
|
||||
self.loadMessagelist(messagelist, account, folder)
|
||||
|
||||
def treeWidgetItemChanged(self, item, column):
|
||||
# only for manual edits. automatic edits (setText) are ignored
|
||||
if column != 0:
|
||||
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
|
||||
|
||||
# recursion prevention
|
||||
if self.recurDepth > 0:
|
||||
return
|
||||
|
||||
self.recurDepth += 1
|
||||
item.setData(0, QtCore.Qt.EditRole, newLabel)
|
||||
item.updateText()
|
||||
if item.type == AccountMixin.MAILINGLIST:
|
||||
self.rerenderComboBoxSendFromBroadcast()
|
||||
elif item.type != AccountMixin.SUBSCRIPTION:
|
||||
self.rerenderComboBoxSendFrom()
|
||||
self.recurDepth -= 1
|
||||
|
||||
def tableWidgetInboxItemClicked(self):
|
||||
currentRow = self.ui.tableWidgetInbox.currentRow()
|
||||
if currentRow >= 0:
|
||||
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)
|
||||
self.ui.textEditInboxMessage.setCurrentFont(font)
|
||||
# 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)
|
||||
|
||||
fromAddress = str(self.ui.tableWidgetInbox.item(
|
||||
currentRow, 1).data(Qt.UserRole).toPyObject())
|
||||
msgid = str(self.ui.tableWidgetInbox.item(
|
||||
currentRow, 3).data(Qt.UserRole).toPyObject())
|
||||
queryreturn = sqlQuery(
|
||||
'''select message from inbox where msgid=?''', msgid)
|
||||
if queryreturn != []:
|
||||
for row in queryreturn:
|
||||
messageText, = row
|
||||
messageText = shared.fixPotentiallyInvalidUTF8Data(messageText)
|
||||
messageText = unicode(messageText, 'utf-8)')
|
||||
if len(messageText) > 30000:
|
||||
messageText = (
|
||||
messageText[:30000] + '\n' +
|
||||
'--- Display of the remainder of the message ' +
|
||||
'truncated because it is too long.\n' +
|
||||
'--- To see the full message, right-click in the ' +
|
||||
'Inbox view and select "View HTML code as formatted ' +
|
||||
'text",\n' +
|
||||
'--- or select "Save message as..." to save it to a ' +
|
||||
'file, or select "Reply" and ' +
|
||||
'view the full message in the quote.')
|
||||
# If we have received this message from either a broadcast address
|
||||
# or from someone in our address book, display as HTML
|
||||
if decodeAddress(fromAddress)[3] in shared.broadcastSendersForWhichImWatching or shared.isAddressInMyAddressBook(fromAddress):
|
||||
self.ui.textEditInboxMessage.setText(messageText)
|
||||
else:
|
||||
self.ui.textEditInboxMessage.setPlainText(messageText)
|
||||
|
||||
self.ui.tableWidgetInbox.item(currentRow, 0).setFont(font)
|
||||
self.ui.tableWidgetInbox.item(currentRow, 1).setFont(font)
|
||||
self.ui.tableWidgetInbox.item(currentRow, 2).setFont(font)
|
||||
self.ui.tableWidgetInbox.item(currentRow, 3).setFont(font)
|
||||
|
||||
inventoryHash = str(self.ui.tableWidgetInbox.item(
|
||||
currentRow, 3).data(Qt.UserRole).toPyObject())
|
||||
self.ubuntuMessagingMenuClear(inventoryHash)
|
||||
sqlExecute('''update inbox set read=1 WHERE msgid=?''', inventoryHash)
|
||||
self.changedInboxUnread()
|
||||
|
||||
def tableWidgetSentItemClicked(self):
|
||||
currentRow = self.ui.tableWidgetSent.currentRow()
|
||||
if currentRow >= 0:
|
||||
ackdata = str(self.ui.tableWidgetSent.item(
|
||||
currentRow, 3).data(Qt.UserRole).toPyObject())
|
||||
queryreturn = sqlQuery(
|
||||
'''select message from sent where ackdata=?''', ackdata)
|
||||
if queryreturn != []:
|
||||
for row in queryreturn:
|
||||
message, = row
|
||||
else:
|
||||
data = self.getCurrentMessageId()
|
||||
if data != False:
|
||||
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.rerenderInboxFromLabels()
|
||||
self.rerenderInboxToLabels()
|
||||
self.rerenderSentFromLabels()
|
||||
# self.rerenderSentToLabels()
|
||||
messageTextedit.setCurrentFont(QtGui.QFont())
|
||||
messageTextedit.setTextColor(QtGui.QColor())
|
||||
messageTextedit.setPlainText(message)
|
||||
|
||||
def tableWidgetAddressBookItemChanged(self):
|
||||
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=?''',
|
||||
str(self.ui.tableWidgetAddressBook.item(currentRow, 0).text().toUtf8()),
|
||||
str(addressAtCurrentRow))
|
||||
self.rerenderInboxFromLabels()
|
||||
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.ui.tableWidgetAddressBook.item(currentRow, 0).setLabel(str(self.ui.tableWidgetAddressBook.item(currentRow, 0).text().toUtf8()))
|
||||
self.rerenderInboxFromLabels()
|
||||
self.rerenderSentToLabels()
|
||||
|
||||
def writeNewAddressToTable(self, label, address, streamNumber):
|
||||
self.ui.tableWidgetYourIdentities.setSortingEnabled(False)
|
||||
self.ui.tableWidgetYourIdentities.insertRow(0)
|
||||
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
|
||||
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.rerenderTabTreeMessages()
|
||||
self.rerenderTabTreeSubscriptions()
|
||||
self.rerenderTabTreeChans()
|
||||
self.rerenderComboBoxSendFrom()
|
||||
self.rerenderComboBoxSendFromBroadcast()
|
||||
|
||||
def updateStatusBar(self, data):
|
||||
if data != "":
|
||||
with shared.printLock:
|
||||
print 'Status bar:', data
|
||||
logger.info('Status bar: ' + 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):
|
||||
|
||||
|
@ -3494,6 +4110,8 @@ class settingsDialog(QtGui.QDialog):
|
|||
# On the Network settings tab:
|
||||
self.ui.lineEditTCPPort.setText(str(
|
||||
shared.config.get('bitmessagesettings', 'port')))
|
||||
self.ui.checkBoxUPnP.setChecked(
|
||||
shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'))
|
||||
self.ui.checkBoxAuthentication.setChecked(shared.config.getboolean(
|
||||
'bitmessagesettings', 'socksauthentication'))
|
||||
self.ui.checkBoxSocksListen.setChecked(shared.config.getboolean(
|
||||
|
@ -3540,6 +4158,16 @@ class settingsDialog(QtGui.QDialog):
|
|||
self.ui.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(shared.config.getint(
|
||||
'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
|
||||
nmctype = shared.config.get('bitmessagesettings', 'namecoinrpctype')
|
||||
self.ui.lineEditNamecoinHost.setText(str(
|
||||
|
@ -3666,9 +4294,7 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
|
|||
self.ui = Ui_SpecialAddressBehaviorDialog()
|
||||
self.ui.setupUi(self)
|
||||
self.parent = parent
|
||||
currentRow = parent.ui.tableWidgetYourIdentities.currentRow()
|
||||
addressAtCurrentRow = str(
|
||||
parent.ui.tableWidgetYourIdentities.item(currentRow, 1).text())
|
||||
addressAtCurrentRow = parent.getCurrentAccount()
|
||||
if not shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'):
|
||||
if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'):
|
||||
self.ui.radioButtonBehaviorMailingList.click()
|
||||
|
@ -3688,6 +4314,25 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
|
|||
|
||||
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):
|
||||
|
||||
|
@ -3799,10 +4444,10 @@ class NewAddressDialog(QtGui.QDialog):
|
|||
row = 1
|
||||
# Let's fill out the 'existing address' combo box with addresses from
|
||||
# the 'Your Identities' tab.
|
||||
while self.parent.ui.tableWidgetYourIdentities.item(row - 1, 1):
|
||||
for addressInKeysFile in getSortedAccounts():
|
||||
self.ui.radioButtonExisting.click()
|
||||
self.ui.comboBoxExisting.addItem(
|
||||
self.parent.ui.tableWidgetYourIdentities.item(row - 1, 1).text())
|
||||
addressInKeysFile)
|
||||
row += 1
|
||||
self.ui.groupBoxDeterministic.setHidden(True)
|
||||
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
|
||||
|
@ -3894,6 +4539,8 @@ class UISignaler(QThread):
|
|||
self.emit(SIGNAL("rerenderBlackWhiteList()"))
|
||||
elif command == 'removeInboxRowByMsgid':
|
||||
self.emit(SIGNAL("removeInboxRowByMsgid(PyQt_PyObject)"), data)
|
||||
elif command == 'newVersionAvailable':
|
||||
self.emit(SIGNAL("newVersionAvailable(PyQt_PyObject)"), data)
|
||||
elif command == 'alert':
|
||||
title, text, exitAfterUserClicksOk = data
|
||||
self.emit(SIGNAL("displayAlert(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"), title, text, exitAfterUserClicksOk)
|
||||
|
@ -3901,18 +4548,88 @@ class UISignaler(QThread):
|
|||
sys.stderr.write(
|
||||
'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():
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
global myapp
|
||||
app = init()
|
||||
change_translation(l10n.getTranslationLanguage())
|
||||
app.setStyleSheet("QStatusBar::item { border: 0px solid black }")
|
||||
myapp = MyForm()
|
||||
|
||||
if not shared.config.getboolean('bitmessagesettings', 'startintray'):
|
||||
myapp.show()
|
||||
|
||||
myapp.appIndicatorInit(app)
|
||||
myapp.ubuntuMessagingMenuInit()
|
||||
myapp.notifierInit()
|
||||
if shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
|
||||
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_())
|
||||
|
|
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'
|
||||
#
|
||||
# Created: Sun Mar 08 22:07:43 2015
|
||||
# by: PyQt4 UI code generator 4.10.3
|
||||
# Created: Mon Mar 23 22:18:07 2015
|
||||
# by: PyQt4 UI code generator 4.10.4
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
import settingsmixin
|
||||
|
||||
try:
|
||||
_fromUtf8 = QtCore.QString.fromUtf8
|
||||
|
@ -33,9 +34,8 @@ class Ui_MainWindow(object):
|
|||
MainWindow.setTabShape(QtGui.QTabWidget.Rounded)
|
||||
self.centralwidget = QtGui.QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
|
||||
self.gridLayout = QtGui.QGridLayout(self.centralwidget)
|
||||
self.gridLayout.setMargin(0)
|
||||
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
|
||||
self.gridLayout_10 = QtGui.QGridLayout(self.centralwidget)
|
||||
self.gridLayout_10.setObjectName(_fromUtf8("gridLayout_10"))
|
||||
self.tabWidget = QtGui.QTabWidget(self.centralwidget)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
|
@ -52,27 +52,52 @@ class Ui_MainWindow(object):
|
|||
self.tabWidget.setObjectName(_fromUtf8("tabWidget"))
|
||||
self.inbox = QtGui.QWidget()
|
||||
self.inbox.setObjectName(_fromUtf8("inbox"))
|
||||
self.verticalLayout_2 = QtGui.QVBoxLayout(self.inbox)
|
||||
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
|
||||
self.horizontalLayoutSearch = QtGui.QHBoxLayout()
|
||||
self.horizontalLayoutSearch.setContentsMargins(-1, 0, -1, -1)
|
||||
self.horizontalLayoutSearch.setObjectName(_fromUtf8("horizontalLayoutSearch"))
|
||||
self.gridLayout = QtGui.QGridLayout(self.inbox)
|
||||
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
|
||||
self.horizontalSplitter_3 = settingsmixin.SSplitter()
|
||||
self.horizontalSplitter_3.setObjectName(_fromUtf8("horizontalSplitter_3"))
|
||||
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.setObjectName(_fromUtf8("inboxSearchLineEdit"))
|
||||
self.horizontalLayoutSearch.addWidget(self.inboxSearchLineEdit)
|
||||
self.inboxSearchOptionCB = QtGui.QComboBox(self.inbox)
|
||||
self.inboxSearchOptionCB.setObjectName(_fromUtf8("inboxSearchOptionCB"))
|
||||
self.inboxSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.inboxSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.inboxSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.inboxSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.inboxSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.horizontalLayoutSearch.addWidget(self.inboxSearchOptionCB)
|
||||
self.verticalLayout_2.addLayout(self.horizontalLayoutSearch)
|
||||
self.splitter = QtGui.QSplitter(self.inbox)
|
||||
self.splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
self.splitter.setObjectName(_fromUtf8("splitter"))
|
||||
self.tableWidgetInbox = QtGui.QTableWidget(self.splitter)
|
||||
self.horizontalSplitterSearch.addWidget(self.inboxSearchLineEdit)
|
||||
self.inboxSearchOption = QtGui.QComboBox(self.inbox)
|
||||
self.inboxSearchOption.setObjectName(_fromUtf8("inboxSearchOption"))
|
||||
self.inboxSearchOption.addItem(_fromUtf8(""))
|
||||
self.inboxSearchOption.addItem(_fromUtf8(""))
|
||||
self.inboxSearchOption.addItem(_fromUtf8(""))
|
||||
self.inboxSearchOption.addItem(_fromUtf8(""))
|
||||
self.inboxSearchOption.addItem(_fromUtf8(""))
|
||||
self.inboxSearchOption.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
|
||||
self.horizontalSplitterSearch.addWidget(self.inboxSearchOption)
|
||||
self.horizontalSplitterSearch.handle(1).setEnabled(False)
|
||||
self.horizontalSplitterSearch.setStretchFactor(0, 1)
|
||||
self.horizontalSplitterSearch.setStretchFactor(1, 0)
|
||||
self.verticalSplitter_7.addWidget(self.horizontalSplitterSearch)
|
||||
self.tableWidgetInbox = settingsmixin.STableWidget(self.inbox)
|
||||
self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
|
||||
self.tableWidgetInbox.setAlternatingRowColors(True)
|
||||
self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
|
||||
|
@ -97,84 +122,170 @@ class Ui_MainWindow(object):
|
|||
self.tableWidgetInbox.horizontalHeader().setStretchLastSection(True)
|
||||
self.tableWidgetInbox.verticalHeader().setVisible(False)
|
||||
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.setReadOnly(True)
|
||||
self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage"))
|
||||
self.verticalLayout_2.addWidget(self.splitter)
|
||||
icon1 = QtGui.QIcon()
|
||||
icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.tabWidget.addTab(self.inbox, icon1, _fromUtf8(""))
|
||||
self.verticalSplitter_7.addWidget(self.textEditInboxMessage)
|
||||
self.verticalSplitter_7.setStretchFactor(0, 0)
|
||||
self.verticalSplitter_7.setStretchFactor(1, 1)
|
||||
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.setObjectName(_fromUtf8("send"))
|
||||
self.gridLayout_2 = QtGui.QGridLayout(self.send)
|
||||
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
|
||||
self.pushButtonLoadFromAddressBook = QtGui.QPushButton(self.send)
|
||||
self.gridLayout_7 = QtGui.QGridLayout(self.send)
|
||||
self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7"))
|
||||
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.setPointSize(7)
|
||||
self.pushButtonLoadFromAddressBook.setFont(font)
|
||||
self.pushButtonLoadFromAddressBook.setObjectName(_fromUtf8("pushButtonLoadFromAddressBook"))
|
||||
self.gridLayout_2.addWidget(self.pushButtonLoadFromAddressBook, 3, 2, 1, 1)
|
||||
self.label_3 = QtGui.QLabel(self.send)
|
||||
font.setPointSize(9)
|
||||
self.pushButtonFetchNamecoinID.setFont(font)
|
||||
self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID"))
|
||||
self.verticalSplitter_2.addWidget(self.pushButtonFetchNamecoinID)
|
||||
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.gridLayout_2.addWidget(self.label_3, 4, 0, 1, 1)
|
||||
self.pushButtonSend = QtGui.QPushButton(self.send)
|
||||
self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend"))
|
||||
self.gridLayout_2.addWidget(self.pushButtonSend, 7, 8, 1, 1)
|
||||
self.horizontalSliderTTL = QtGui.QSlider(self.send)
|
||||
self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(35, 0))
|
||||
self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(70, 16777215))
|
||||
self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.horizontalSliderTTL.setInvertedAppearance(False)
|
||||
self.horizontalSliderTTL.setInvertedControls(False)
|
||||
self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL"))
|
||||
self.gridLayout_2.addWidget(self.horizontalSliderTTL, 7, 6, 1, 1)
|
||||
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.gridLayout_2.addWidget(self.label_3, 2, 0, 1, 1)
|
||||
self.label_2 = QtGui.QLabel(self.sendDirect)
|
||||
self.label_2.setObjectName(_fromUtf8("label_2"))
|
||||
self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
|
||||
self.lineEditSubject = QtGui.QLineEdit(self.sendDirect)
|
||||
self.lineEditSubject.setText(_fromUtf8(""))
|
||||
self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject"))
|
||||
self.gridLayout_2.addWidget(self.lineEditSubject, 2, 1, 1, 1)
|
||||
self.label = QtGui.QLabel(self.sendDirect)
|
||||
self.label.setObjectName(_fromUtf8("label"))
|
||||
self.gridLayout_2.addWidget(self.label, 1, 0, 1, 1)
|
||||
self.comboBoxSendFrom = QtGui.QComboBox(self.sendDirect)
|
||||
self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0))
|
||||
self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom"))
|
||||
self.gridLayout_2.addWidget(self.comboBoxSendFrom, 2, 1, 1, 1)
|
||||
self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth())
|
||||
self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy)
|
||||
self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0))
|
||||
self.labelHumanFriendlyTTLDescription.setMaximumSize(QtCore.QSize(45, 16777215))
|
||||
self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription"))
|
||||
self.gridLayout_2.addWidget(self.labelHumanFriendlyTTLDescription, 7, 7, 1, 1)
|
||||
self.label_4 = QtGui.QLabel(self.send)
|
||||
self.label_4.setObjectName(_fromUtf8("label_4"))
|
||||
self.gridLayout_2.addWidget(self.label_4, 5, 0, 1, 1)
|
||||
self.label = QtGui.QLabel(self.send)
|
||||
self.label.setObjectName(_fromUtf8("label"))
|
||||
self.gridLayout_2.addWidget(self.label, 3, 0, 1, 1)
|
||||
self.radioButtonSpecific = QtGui.QRadioButton(self.send)
|
||||
self.radioButtonSpecific.setChecked(True)
|
||||
self.radioButtonSpecific.setObjectName(_fromUtf8("radioButtonSpecific"))
|
||||
self.gridLayout_2.addWidget(self.radioButtonSpecific, 0, 1, 1, 1)
|
||||
self.labelSendBroadcastWarning = QtGui.QLabel(self.send)
|
||||
self.labelSendBroadcastWarning.setEnabled(True)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.labelSendBroadcastWarning.sizePolicy().hasHeightForWidth())
|
||||
self.labelSendBroadcastWarning.setSizePolicy(sizePolicy)
|
||||
self.labelSendBroadcastWarning.setIndent(-1)
|
||||
self.labelSendBroadcastWarning.setObjectName(_fromUtf8("labelSendBroadcastWarning"))
|
||||
self.gridLayout_2.addWidget(self.labelSendBroadcastWarning, 7, 1, 1, 4)
|
||||
self.radioButtonBroadcast = QtGui.QRadioButton(self.send)
|
||||
self.radioButtonBroadcast.setObjectName(_fromUtf8("radioButtonBroadcast"))
|
||||
self.gridLayout_2.addWidget(self.radioButtonBroadcast, 1, 1, 1, 2)
|
||||
self.gridLayout_2.addWidget(self.comboBoxSendFrom, 0, 1, 1, 1)
|
||||
self.lineEditTo = QtGui.QLineEdit(self.sendDirect)
|
||||
self.lineEditTo.setObjectName(_fromUtf8("lineEditTo"))
|
||||
self.gridLayout_2.addWidget(self.lineEditTo, 1, 1, 1, 1)
|
||||
self.gridLayout_2_Widget = QtGui.QWidget()
|
||||
self.gridLayout_2_Widget.setLayout(self.gridLayout_2)
|
||||
self.verticalSplitter_5.addWidget(self.gridLayout_2_Widget)
|
||||
self.textEditMessage = QtGui.QTextEdit(self.sendDirect)
|
||||
self.textEditMessage.setObjectName(_fromUtf8("textEditMessage"))
|
||||
self.verticalSplitter_5.addWidget(self.textEditMessage)
|
||||
self.verticalSplitter_5.setStretchFactor(0, 0)
|
||||
self.verticalSplitter_5.setStretchFactor(1, 1)
|
||||
self.verticalSplitter_5.setCollapsible(0, False)
|
||||
self.verticalSplitter_5.setCollapsible(1, False)
|
||||
self.verticalSplitter_5.handle(1).setEnabled(False)
|
||||
self.gridLayout_8.addWidget(self.verticalSplitter_5, 0, 0, 1, 1)
|
||||
self.tabWidgetSend.addTab(self.sendDirect, _fromUtf8(""))
|
||||
self.sendBroadcast = QtGui.QWidget()
|
||||
self.sendBroadcast.setObjectName(_fromUtf8("sendBroadcast"))
|
||||
self.gridLayout_9 = QtGui.QGridLayout(self.sendBroadcast)
|
||||
self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9"))
|
||||
self.verticalSplitter_6 = settingsmixin.SSplitter()
|
||||
self.verticalSplitter_6.setObjectName(_fromUtf8("verticalSplitter_6"))
|
||||
self.verticalSplitter_6.setOrientation(QtCore.Qt.Vertical)
|
||||
self.gridLayout_5 = QtGui.QGridLayout()
|
||||
self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5"))
|
||||
self.label_8 = QtGui.QLabel(self.sendBroadcast)
|
||||
self.label_8.setObjectName(_fromUtf8("label_8"))
|
||||
self.gridLayout_5.addWidget(self.label_8, 0, 0, 1, 1)
|
||||
self.lineEditSubjectBroadcast = QtGui.QLineEdit(self.sendBroadcast)
|
||||
self.lineEditSubjectBroadcast.setText(_fromUtf8(""))
|
||||
self.lineEditSubjectBroadcast.setObjectName(_fromUtf8("lineEditSubjectBroadcast"))
|
||||
self.gridLayout_5.addWidget(self.lineEditSubjectBroadcast, 1, 1, 1, 1)
|
||||
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)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.pushButtonTTL.sizePolicy().hasHeightForWidth())
|
||||
self.pushButtonTTL.setSizePolicy(sizePolicy)
|
||||
self.pushButtonTTL.setMaximumSize(QtCore.QSize(32, 16777215))
|
||||
# sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
|
||||
# sizePolicy.setHorizontalStretch(0)
|
||||
# sizePolicy.setVerticalStretch(0)
|
||||
# sizePolicy.setHeightForWidth(self.pushButtonTTL.sizePolicy().hasHeightForWidth())
|
||||
# self.pushButtonTTL.setSizePolicy(sizePolicy)
|
||||
palette = QtGui.QPalette()
|
||||
brush = QtGui.QBrush(QtGui.QColor(0, 0, 255))
|
||||
brush.setStyle(QtCore.Qt.SolidPattern)
|
||||
|
@ -191,194 +302,252 @@ class Ui_MainWindow(object):
|
|||
self.pushButtonTTL.setFont(font)
|
||||
self.pushButtonTTL.setFlat(True)
|
||||
self.pushButtonTTL.setObjectName(_fromUtf8("pushButtonTTL"))
|
||||
self.gridLayout_2.addWidget(self.pushButtonTTL, 7, 5, 1, 1)
|
||||
self.label_2 = QtGui.QLabel(self.send)
|
||||
self.label_2.setObjectName(_fromUtf8("label_2"))
|
||||
self.gridLayout_2.addWidget(self.label_2, 2, 0, 1, 1)
|
||||
self.lineEditTo = QtGui.QLineEdit(self.send)
|
||||
self.lineEditTo.setObjectName(_fromUtf8("lineEditTo"))
|
||||
self.gridLayout_2.addWidget(self.lineEditTo, 3, 1, 1, 1)
|
||||
self.textEditMessage = QtGui.QTextEdit(self.send)
|
||||
self.textEditMessage.setObjectName(_fromUtf8("textEditMessage"))
|
||||
self.gridLayout_2.addWidget(self.textEditMessage, 5, 1, 2, 8)
|
||||
self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send)
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(7)
|
||||
self.pushButtonFetchNamecoinID.setFont(font)
|
||||
self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID"))
|
||||
self.gridLayout_2.addWidget(self.pushButtonFetchNamecoinID, 3, 3, 1, 1)
|
||||
self.labelFrom = QtGui.QLabel(self.send)
|
||||
self.labelFrom.setText(_fromUtf8(""))
|
||||
self.labelFrom.setObjectName(_fromUtf8("labelFrom"))
|
||||
self.gridLayout_2.addWidget(self.labelFrom, 2, 2, 1, 7)
|
||||
self.lineEditSubject = QtGui.QLineEdit(self.send)
|
||||
self.lineEditSubject.setText(_fromUtf8(""))
|
||||
self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject"))
|
||||
self.gridLayout_2.addWidget(self.lineEditSubject, 4, 1, 1, 8)
|
||||
icon2 = QtGui.QIcon()
|
||||
icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.tabWidget.addTab(self.send, icon2, _fromUtf8(""))
|
||||
self.sent = QtGui.QWidget()
|
||||
self.sent.setObjectName(_fromUtf8("sent"))
|
||||
self.verticalLayout = QtGui.QVBoxLayout(self.sent)
|
||||
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
|
||||
self.horizontalLayout = QtGui.QHBoxLayout()
|
||||
self.horizontalLayout.setContentsMargins(-1, 0, -1, -1)
|
||||
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
|
||||
self.sentSearchLineEdit = QtGui.QLineEdit(self.sent)
|
||||
self.sentSearchLineEdit.setObjectName(_fromUtf8("sentSearchLineEdit"))
|
||||
self.horizontalLayout.addWidget(self.sentSearchLineEdit)
|
||||
self.sentSearchOptionCB = QtGui.QComboBox(self.sent)
|
||||
self.sentSearchOptionCB.setObjectName(_fromUtf8("sentSearchOptionCB"))
|
||||
self.sentSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.sentSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.sentSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.sentSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.sentSearchOptionCB.addItem(_fromUtf8(""))
|
||||
self.horizontalLayout.addWidget(self.sentSearchOptionCB)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
self.splitter_2 = QtGui.QSplitter(self.sent)
|
||||
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)
|
||||
self.horizontalSplitter_5.addWidget(self.pushButtonTTL)
|
||||
self.horizontalSliderTTL = QtGui.QSlider(self.send)
|
||||
self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(35, 0))
|
||||
self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.horizontalSliderTTL.setInvertedAppearance(False)
|
||||
self.horizontalSliderTTL.setInvertedControls(False)
|
||||
self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL"))
|
||||
self.horizontalSplitter_5.addWidget(self.horizontalSliderTTL)
|
||||
self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send)
|
||||
# sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred)
|
||||
# sizePolicy.setHorizontalStretch(0)
|
||||
# sizePolicy.setVerticalStretch(0)
|
||||
# sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth())
|
||||
# self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy)
|
||||
self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0))
|
||||
self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription"))
|
||||
self.horizontalSplitter_5.addWidget(self.labelHumanFriendlyTTLDescription)
|
||||
self.pushButtonSend = QtGui.QPushButton(self.send)
|
||||
self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend"))
|
||||
self.horizontalSplitter_5.addWidget(self.pushButtonSend)
|
||||
self.pushButtonTTL.setMaximumSize(QtCore.QSize(32, self.pushButtonSend.height()))
|
||||
self.labelHumanFriendlyTTLDescription.setMaximumSize(QtCore.QSize(45, self.pushButtonSend.height()))
|
||||
self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(70, self.pushButtonSend.height()))
|
||||
self.horizontalSplitter_5.resize(self.horizontalSplitter_5.width(), self.pushButtonSend.height())
|
||||
self.horizontalSplitter_5.setStretchFactor(0, 0)
|
||||
self.horizontalSplitter_5.setStretchFactor(1, 0)
|
||||
self.horizontalSplitter_5.setStretchFactor(2, 0)
|
||||
self.horizontalSplitter_5.setStretchFactor(3, 1)
|
||||
self.horizontalSplitter_5.setCollapsible(0, False)
|
||||
self.horizontalSplitter_5.setCollapsible(1, False)
|
||||
self.horizontalSplitter_5.setCollapsible(2, False)
|
||||
self.horizontalSplitter_5.setCollapsible(3, False)
|
||||
self.horizontalSplitter_5.handle(1).setEnabled(False)
|
||||
self.horizontalSplitter_5.handle(2).setEnabled(False)
|
||||
self.horizontalSplitter_5.handle(3).setEnabled(False)
|
||||
self.verticalSplitter.addWidget(self.horizontalSplitter_5)
|
||||
self.verticalSplitter.setStretchFactor(0, 0)
|
||||
self.verticalSplitter.setStretchFactor(1, 1)
|
||||
self.verticalSplitter.setCollapsible(0, False)
|
||||
self.verticalSplitter.setCollapsible(1, False)
|
||||
self.verticalSplitter.handle(1).setEnabled(False)
|
||||
self.horizontalSplitter.addWidget(self.verticalSplitter)
|
||||
self.horizontalSplitter.setStretchFactor(0, 0)
|
||||
self.horizontalSplitter.setStretchFactor(1, 1)
|
||||
self.horizontalSplitter.setCollapsible(0, False)
|
||||
self.horizontalSplitter.setCollapsible(1, False)
|
||||
self.gridLayout_7.addWidget(self.horizontalSplitter, 0, 0, 1, 1)
|
||||
icon4 = QtGui.QIcon()
|
||||
icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.tabWidget.addTab(self.youridentities, icon4, _fromUtf8(""))
|
||||
icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.tabWidget.addTab(self.send, icon4, _fromUtf8(""))
|
||||
self.subscriptions = QtGui.QWidget()
|
||||
self.subscriptions.setObjectName(_fromUtf8("subscriptions"))
|
||||
self.gridLayout_4 = QtGui.QGridLayout(self.subscriptions)
|
||||
self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4"))
|
||||
self.label_5 = QtGui.QLabel(self.subscriptions)
|
||||
self.label_5.setWordWrap(True)
|
||||
self.label_5.setObjectName(_fromUtf8("label_5"))
|
||||
self.gridLayout_4.addWidget(self.label_5, 0, 0, 1, 2)
|
||||
self.gridLayout_3 = QtGui.QGridLayout(self.subscriptions)
|
||||
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
|
||||
self.horizontalSplitter_4 = settingsmixin.SSplitter()
|
||||
self.horizontalSplitter_4.setObjectName(_fromUtf8("horizontalSplitter_4"))
|
||||
self.verticalSplitter_3 = settingsmixin.SSplitter()
|
||||
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.setObjectName(_fromUtf8("pushButtonAddSubscription"))
|
||||
self.gridLayout_4.addWidget(self.pushButtonAddSubscription, 1, 0, 1, 1)
|
||||
spacerItem2 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.gridLayout_4.addItem(spacerItem2, 1, 1, 1, 1)
|
||||
self.tableWidgetSubscriptions = QtGui.QTableWidget(self.subscriptions)
|
||||
self.tableWidgetSubscriptions.setAlternatingRowColors(True)
|
||||
self.tableWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
|
||||
self.tableWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
|
||||
self.tableWidgetSubscriptions.setObjectName(_fromUtf8("tableWidgetSubscriptions"))
|
||||
self.tableWidgetSubscriptions.setColumnCount(2)
|
||||
self.tableWidgetSubscriptions.setRowCount(0)
|
||||
self.pushButtonAddSubscription.resize(200, self.pushButtonAddSubscription.height())
|
||||
self.verticalSplitter_3.addWidget(self.pushButtonAddSubscription)
|
||||
self.verticalSplitter_3.setStretchFactor(0, 1)
|
||||
self.verticalSplitter_3.setStretchFactor(1, 0)
|
||||
self.verticalSplitter_3.setCollapsible(0, False)
|
||||
self.verticalSplitter_3.setCollapsible(1, False)
|
||||
self.verticalSplitter_3.handle(1).setEnabled(False)
|
||||
self.horizontalSplitter_4.addWidget(self.verticalSplitter_3)
|
||||
self.verticalSplitter_4 = settingsmixin.SSplitter()
|
||||
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()
|
||||
self.tableWidgetSubscriptions.setHorizontalHeaderItem(0, item)
|
||||
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(0, item)
|
||||
item = QtGui.QTableWidgetItem()
|
||||
self.tableWidgetSubscriptions.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)
|
||||
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(1, item)
|
||||
item = QtGui.QTableWidgetItem()
|
||||
self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item)
|
||||
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(2, item)
|
||||
item = QtGui.QTableWidgetItem()
|
||||
self.tableWidgetAddressBook.setHorizontalHeaderItem(1, item)
|
||||
self.tableWidgetAddressBook.horizontalHeader().setCascadingSectionResizes(True)
|
||||
self.tableWidgetAddressBook.horizontalHeader().setDefaultSectionSize(400)
|
||||
self.tableWidgetAddressBook.horizontalHeader().setHighlightSections(False)
|
||||
self.tableWidgetAddressBook.horizontalHeader().setStretchLastSection(True)
|
||||
self.tableWidgetAddressBook.verticalHeader().setVisible(False)
|
||||
self.gridLayout_5.addWidget(self.tableWidgetAddressBook, 2, 0, 1, 2)
|
||||
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(3, item)
|
||||
self.tableWidgetInboxSubscriptions.horizontalHeader().setCascadingSectionResizes(True)
|
||||
self.tableWidgetInboxSubscriptions.horizontalHeader().setDefaultSectionSize(200)
|
||||
self.tableWidgetInboxSubscriptions.horizontalHeader().setHighlightSections(False)
|
||||
self.tableWidgetInboxSubscriptions.horizontalHeader().setMinimumSectionSize(27)
|
||||
self.tableWidgetInboxSubscriptions.horizontalHeader().setSortIndicatorShown(False)
|
||||
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.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.tabWidget.addTab(self.addressbook, icon6, _fromUtf8(""))
|
||||
icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
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.setObjectName(_fromUtf8("blackwhitelist"))
|
||||
self.gridLayout_6 = QtGui.QGridLayout(self.blackwhitelist)
|
||||
|
@ -393,9 +562,9 @@ class Ui_MainWindow(object):
|
|||
self.pushButtonAddBlacklist = QtGui.QPushButton(self.blackwhitelist)
|
||||
self.pushButtonAddBlacklist.setObjectName(_fromUtf8("pushButtonAddBlacklist"))
|
||||
self.gridLayout_6.addWidget(self.pushButtonAddBlacklist, 2, 0, 1, 1)
|
||||
spacerItem4 = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.gridLayout_6.addItem(spacerItem4, 2, 1, 1, 1)
|
||||
self.tableWidgetBlacklist = QtGui.QTableWidget(self.blackwhitelist)
|
||||
spacerItem = QtGui.QSpacerItem(689, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.gridLayout_6.addItem(spacerItem, 2, 1, 1, 1)
|
||||
self.tableWidgetBlacklist = settingsmixin.STableWidget(self.blackwhitelist)
|
||||
self.tableWidgetBlacklist.setAlternatingRowColors(True)
|
||||
self.tableWidgetBlacklist.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
|
||||
self.tableWidgetBlacklist.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
|
||||
|
@ -413,20 +582,20 @@ class Ui_MainWindow(object):
|
|||
self.tableWidgetBlacklist.horizontalHeader().setStretchLastSection(True)
|
||||
self.tableWidgetBlacklist.verticalHeader().setVisible(False)
|
||||
self.gridLayout_6.addWidget(self.tableWidgetBlacklist, 3, 0, 1, 2)
|
||||
icon7 = QtGui.QIcon()
|
||||
icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/blacklist.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.tabWidget.addTab(self.blackwhitelist, icon7, _fromUtf8(""))
|
||||
icon9 = QtGui.QIcon()
|
||||
icon9.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/blacklist.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.tabWidget.addTab(self.blackwhitelist, icon9, _fromUtf8(""))
|
||||
self.networkstatus = QtGui.QWidget()
|
||||
self.networkstatus.setObjectName(_fromUtf8("networkstatus"))
|
||||
self.pushButtonStatusIcon = QtGui.QPushButton(self.networkstatus)
|
||||
self.pushButtonStatusIcon.setGeometry(QtCore.QRect(680, 440, 21, 23))
|
||||
self.pushButtonStatusIcon.setText(_fromUtf8(""))
|
||||
icon8 = QtGui.QIcon()
|
||||
icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/redicon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.pushButtonStatusIcon.setIcon(icon8)
|
||||
icon10 = QtGui.QIcon()
|
||||
icon10.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/redicon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.pushButtonStatusIcon.setIcon(icon10)
|
||||
self.pushButtonStatusIcon.setFlat(True)
|
||||
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))
|
||||
palette = QtGui.QPalette()
|
||||
brush = QtGui.QBrush(QtGui.QColor(212, 208, 200))
|
||||
|
@ -470,22 +639,25 @@ class Ui_MainWindow(object):
|
|||
self.labelBroadcastCount = QtGui.QLabel(self.networkstatus)
|
||||
self.labelBroadcastCount.setGeometry(QtCore.QRect(350, 150, 351, 16))
|
||||
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.setGeometry(QtCore.QRect(320, 250, 291, 16))
|
||||
self.labelLookupsPerSecond.setGeometry(QtCore.QRect(320, 270, 291, 16))
|
||||
self.labelLookupsPerSecond.setObjectName(_fromUtf8("labelLookupsPerSecond"))
|
||||
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.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"))
|
||||
icon9 = QtGui.QIcon()
|
||||
icon9.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.tabWidget.addTab(self.networkstatus, icon9, _fromUtf8(""))
|
||||
self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1)
|
||||
icon11 = QtGui.QIcon()
|
||||
icon11.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/networkstatus.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.tabWidget.addTab(self.networkstatus, icon11, _fromUtf8(""))
|
||||
self.gridLayout_10.addWidget(self.tabWidget, 0, 0, 1, 1)
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
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.menuFile = QtGui.QMenu(self.menubar)
|
||||
self.menuFile.setObjectName(_fromUtf8("menuFile"))
|
||||
|
@ -537,7 +709,6 @@ class Ui_MainWindow(object):
|
|||
self.menuFile.addAction(self.actionManageKeys)
|
||||
self.menuFile.addAction(self.actionDeleteAllTrashedMessages)
|
||||
self.menuFile.addAction(self.actionRegenerateDeterministicAddresses)
|
||||
self.menuFile.addAction(self.actionJoinChan)
|
||||
self.menuFile.addAction(self.actionExit)
|
||||
self.menuSettings.addAction(self.actionSettings)
|
||||
self.menuHelp.addAction(self.actionHelp)
|
||||
|
@ -548,29 +719,15 @@ class Ui_MainWindow(object):
|
|||
|
||||
self.retranslateUi(MainWindow)
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
QtCore.QObject.connect(self.radioButtonSpecific, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.lineEditTo.setEnabled)
|
||||
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)
|
||||
self.tabWidgetSend.setCurrentIndex(0)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
MainWindow.setTabOrder(self.tabWidget, self.tableWidgetInbox)
|
||||
MainWindow.setTabOrder(self.tableWidgetInbox, self.textEditInboxMessage)
|
||||
MainWindow.setTabOrder(self.textEditInboxMessage, self.radioButtonSpecific)
|
||||
MainWindow.setTabOrder(self.radioButtonSpecific, self.radioButtonBroadcast)
|
||||
MainWindow.setTabOrder(self.radioButtonBroadcast, self.comboBoxSendFrom)
|
||||
MainWindow.setTabOrder(self.textEditInboxMessage, self.comboBoxSendFrom)
|
||||
MainWindow.setTabOrder(self.comboBoxSendFrom, self.lineEditTo)
|
||||
MainWindow.setTabOrder(self.lineEditTo, self.pushButtonLoadFromAddressBook)
|
||||
MainWindow.setTabOrder(self.pushButtonLoadFromAddressBook, self.lineEditSubject)
|
||||
MainWindow.setTabOrder(self.lineEditTo, self.lineEditSubject)
|
||||
MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage)
|
||||
MainWindow.setTabOrder(self.textEditMessage, self.pushButtonSend)
|
||||
MainWindow.setTabOrder(self.pushButtonSend, self.tableWidgetSent)
|
||||
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.textEditMessage, self.pushButtonAddSubscription)
|
||||
MainWindow.setTabOrder(self.pushButtonAddSubscription, self.radioButtonBlacklist)
|
||||
MainWindow.setTabOrder(self.radioButtonBlacklist, self.radioButtonWhitelist)
|
||||
MainWindow.setTabOrder(self.radioButtonWhitelist, self.pushButtonAddBlacklist)
|
||||
MainWindow.setTabOrder(self.pushButtonAddBlacklist, self.tableWidgetBlacklist)
|
||||
|
@ -579,12 +736,14 @@ class Ui_MainWindow(object):
|
|||
|
||||
def retranslateUi(self, MainWindow):
|
||||
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.inboxSearchOptionCB.setItemText(0, _translate("MainWindow", "All", None))
|
||||
self.inboxSearchOptionCB.setItemText(1, _translate("MainWindow", "To", None))
|
||||
self.inboxSearchOptionCB.setItemText(2, _translate("MainWindow", "From", None))
|
||||
self.inboxSearchOptionCB.setItemText(3, _translate("MainWindow", "Subject", None))
|
||||
self.inboxSearchOptionCB.setItemText(4, _translate("MainWindow", "Message", None))
|
||||
self.inboxSearchOption.setItemText(0, _translate("MainWindow", "All", None))
|
||||
self.inboxSearchOption.setItemText(1, _translate("MainWindow", "To", None))
|
||||
self.inboxSearchOption.setItemText(2, _translate("MainWindow", "From", None))
|
||||
self.inboxSearchOption.setItemText(3, _translate("MainWindow", "Subject", None))
|
||||
self.inboxSearchOption.setItemText(4, _translate("MainWindow", "Message", None))
|
||||
self.tableWidgetInbox.setSortingEnabled(True)
|
||||
item = self.tableWidgetInbox.horizontalHeaderItem(0)
|
||||
item.setText(_translate("MainWindow", "To", None))
|
||||
|
@ -594,66 +753,71 @@ class Ui_MainWindow(object):
|
|||
item.setText(_translate("MainWindow", "Subject", None))
|
||||
item = self.tableWidgetInbox.horizontalHeaderItem(3)
|
||||
item.setText(_translate("MainWindow", "Received", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Inbox", None))
|
||||
self.pushButtonLoadFromAddressBook.setText(_translate("MainWindow", "Load from Address book", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.inbox), _translate("MainWindow", "Messages", 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.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.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"
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
|
||||
"p, li { white-space: pre-wrap; }\n"
|
||||
"</style></head><body style=\" font-family:\'MS Shell Dlg 2\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>", None))
|
||||
self.pushButtonFetchNamecoinID.setText(_translate("MainWindow", "Fetch Namecoin ID", None))
|
||||
"</style></head><body style=\" font-family:\'Droid Sans\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'MS Shell Dlg 2\';\"><br /></p></body></html>", None))
|
||||
self.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.sentSearchLineEdit.setPlaceholderText(_translate("MainWindow", "Search", 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.treeWidgetSubscriptions.headerItem().setText(0, _translate("MainWindow", "Subscriptions", None))
|
||||
self.pushButtonAddSubscription.setText(_translate("MainWindow", "Add new Subscription", None))
|
||||
self.tableWidgetSubscriptions.setSortingEnabled(True)
|
||||
item = self.tableWidgetSubscriptions.horizontalHeaderItem(0)
|
||||
item.setText(_translate("MainWindow", "Label", None))
|
||||
item = self.tableWidgetSubscriptions.horizontalHeaderItem(1)
|
||||
item.setText(_translate("MainWindow", "Address", None))
|
||||
self.inboxSearchLineEditSubscriptions.setPlaceholderText(_translate("MainWindow", "Search", None))
|
||||
self.inboxSearchOptionSubscriptions.setItemText(0, _translate("MainWindow", "All", None))
|
||||
self.inboxSearchOptionSubscriptions.setItemText(1, _translate("MainWindow", "To", None))
|
||||
self.inboxSearchOptionSubscriptions.setItemText(2, _translate("MainWindow", "From", 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.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.pushButtonAddAddressBook.setText(_translate("MainWindow", "Add new entry", None))
|
||||
self.tableWidgetAddressBook.setSortingEnabled(True)
|
||||
item = self.tableWidgetAddressBook.horizontalHeaderItem(0)
|
||||
item.setText(_translate("MainWindow", "Name or Label", None))
|
||||
item = self.tableWidgetAddressBook.horizontalHeaderItem(1)
|
||||
item.setText(_translate("MainWindow", "Address", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.addressbook), _translate("MainWindow", "Address Book", None))
|
||||
self.treeWidgetChans.headerItem().setText(0, _translate("MainWindow", "Chans", None))
|
||||
self.pushButtonAddChan.setText(_translate("MainWindow", "Add Chan", None))
|
||||
self.inboxSearchLineEditChans.setPlaceholderText(_translate("MainWindow", "Search", None))
|
||||
self.inboxSearchOptionChans.setItemText(0, _translate("MainWindow", "All", None))
|
||||
self.inboxSearchOptionChans.setItemText(1, _translate("MainWindow", "To", None))
|
||||
self.inboxSearchOptionChans.setItemText(2, _translate("MainWindow", "From", None))
|
||||
self.inboxSearchOptionChans.setItemText(3, _translate("MainWindow", "Subject", 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.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))
|
||||
|
@ -669,6 +833,7 @@ class Ui_MainWindow(object):
|
|||
item.setText(_translate("MainWindow", "Connections", None))
|
||||
self.labelTotalConnections.setText(_translate("MainWindow", "Total connections:", 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.labelPubkeyCount.setText(_translate("MainWindow", "Processed 0 public keys.", 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))
|
||||
|
||||
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>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_10">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="sizePolicy">
|
||||
|
@ -65,9 +62,50 @@
|
|||
<normaloff>:/newPrefix/images/inbox.png</normaloff>:/newPrefix/images/inbox.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Inbox</string>
|
||||
<string>Messages</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeWidgetYourIdentities">
|
||||
<property name="maximumSize">
|
||||
<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>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutSearch">
|
||||
<property name="topMargin">
|
||||
|
@ -81,7 +119,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="inboxSearchOptionCB">
|
||||
<widget class="QComboBox" name="inboxSearchOption">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All</string>
|
||||
|
@ -112,10 +150,6 @@
|
|||
</layout>
|
||||
</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>
|
||||
|
@ -180,6 +214,8 @@
|
|||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEditInboxMessage">
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
|
@ -191,7 +227,10 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -203,72 +242,141 @@
|
|||
<attribute name="title">
|
||||
<string>Send</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="3" column="2">
|
||||
<widget class="QPushButton" name="pushButtonLoadFromAddressBook">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>7</pointsize>
|
||||
</font>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableWidgetAddressBook">
|
||||
<property name="maximumSize">
|
||||
<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>Load from Address book</string>
|
||||
<string>Add Contact</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<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>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidgetSend">
|
||||
<property name="currentIndex">
|
||||
<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="7" column="8">
|
||||
<widget class="QPushButton" name="pushButtonSend">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Send</string>
|
||||
<string>From:</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="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>
|
||||
|
@ -278,82 +386,88 @@
|
|||
</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>
|
||||
<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>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
</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>Message:</string>
|
||||
<string>From:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="lineEditSubjectBroadcast">
|
||||
<property name="text">
|
||||
<string>To:</string>
|
||||
<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="QRadioButton" name="radioButtonSpecific">
|
||||
<property name="text">
|
||||
<string>Send to one or more specific people</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
<widget class="QComboBox" name="comboBoxSendFromBroadcast">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</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>
|
||||
</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>
|
||||
<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>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonTTL">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
|
@ -417,78 +531,148 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>From:</string>
|
||||
<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 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>
|
||||
<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 row="3" column="3">
|
||||
<widget class="QPushButton" name="pushButtonFetchNamecoinID">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>7</pointsize>
|
||||
</font>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonSend">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</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/>
|
||||
<string>Send</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder></zorder>
|
||||
</widget>
|
||||
<widget class="QWidget" name="sent">
|
||||
<widget class="QWidget" name="subscriptions">
|
||||
<attribute name="icon">
|
||||
<iconset resource="bitmessage_icons.qrc">
|
||||
<normaloff>:/newPrefix/images/sent.png</normaloff>:/newPrefix/images/sent.png</iconset>
|
||||
<normaloff>:/newPrefix/images/subscriptions.png</normaloff>:/newPrefix/images/subscriptions.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Sent</string>
|
||||
<string>Subscriptions</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeWidgetSubscriptions">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Subscriptions</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<selectedoff>:/newPrefix/images/subscriptions.png</selectedoff>
|
||||
</iconset>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="sentSearchLineEdit">
|
||||
<widget class="QPushButton" name="pushButtonAddSubscription">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add new Subscription</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="inboxSearchLineEditSubscriptions">
|
||||
<property name="placeholderText">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="sentSearchOptionCB">
|
||||
<widget class="QComboBox" name="inboxSearchOptionSubscriptions">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All</string>
|
||||
|
@ -519,17 +703,10 @@ p, li { white-space: pre-wrap; }
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QTableWidget" name="tableWidgetSent">
|
||||
<widget class="QTableWidget" name="tableWidgetInboxSubscriptions">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DragDrop</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
@ -549,11 +726,14 @@ p, li { white-space: pre-wrap; }
|
|||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>130</number>
|
||||
<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>
|
||||
|
@ -563,8 +743,8 @@ p, li { white-space: pre-wrap; }
|
|||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>26</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
|
@ -583,50 +763,51 @@ p, li { white-space: pre-wrap; }
|
|||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Status</string>
|
||||
<string>Received</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QTextEdit" name="textEditSentMessage">
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="youridentities">
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="icon">
|
||||
<iconset resource="bitmessage_icons.qrc">
|
||||
<normaloff>:/newPrefix/images/identities.png</normaloff>:/newPrefix/images/identities.png</iconset>
|
||||
<normaloff>:/newPrefix/images/can-icon-16px.png</normaloff>:/newPrefix/images/can-icon-16px.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Your Identities</string>
|
||||
<string>Chans</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<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">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_17">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeWidgetChans">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>689</width>
|
||||
<height>20</height>
|
||||
<width>200</width>
|
||||
<height>16777215</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>
|
||||
|
@ -642,186 +823,80 @@ p, li { white-space: pre-wrap; }
|
|||
<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>
|
||||
<string>Chans</string>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<kerning>true</kerning>
|
||||
</font>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<selectedoff>:/newPrefix/images/can-icon-16px.png</selectedoff>
|
||||
</iconset>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonAddChan">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Address</string>
|
||||
<string>Add Chan</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Stream</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="subscriptions">
|
||||
<attribute name="icon">
|
||||
<iconset resource="bitmessage_icons.qrc">
|
||||
<normaloff>:/newPrefix/images/subscriptions.png</normaloff>:/newPrefix/images/subscriptions.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Subscriptions</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<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>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="inboxSearchLineEditChans">
|
||||
<property name="placeholderText">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="pushButtonAddSubscription">
|
||||
<item>
|
||||
<widget class="QComboBox" name="inboxSearchOptionChans">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Add new Subscription</string>
|
||||
<string>All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>To</string>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>689</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QTableWidget" name="tableWidgetSubscriptions">
|
||||
<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>400</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>
|
||||
<column>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Label</string>
|
||||
<string>From</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Address</string>
|
||||
<string>Subject</string>
|
||||
</property>
|
||||
</column>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Message</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="addressbook">
|
||||
<attribute name="icon">
|
||||
<iconset resource="bitmessage_icons.qrc">
|
||||
<normaloff>:/newPrefix/images/addressbook.png</normaloff>:/newPrefix/images/addressbook.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Address Book</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<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>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="pushButtonAddAddressBook">
|
||||
<property name="text">
|
||||
<string>Add new entry</string>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableWidgetInboxChans">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<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="2" column="0" colspan="2">
|
||||
<widget class="QTableWidget" name="tableWidgetAddressBook">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
@ -834,33 +909,72 @@ p, li { white-space: pre-wrap; }
|
|||
<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>400</number>
|
||||
<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>Name or Label</string>
|
||||
<string>To</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Address</string>
|
||||
<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>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="blackwhitelist">
|
||||
|
@ -1182,7 +1296,7 @@ p, li { white-space: pre-wrap; }
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>885</width>
|
||||
<height>21</height>
|
||||
<height>27</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
|
@ -1192,7 +1306,6 @@ p, li { white-space: pre-wrap; }
|
|||
<addaction name="actionManageKeys"/>
|
||||
<addaction name="actionDeleteAllTrashedMessages"/>
|
||||
<addaction name="actionRegenerateDeterministicAddresses"/>
|
||||
<addaction name="actionJoinChan"/>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuSettings">
|
||||
|
@ -1319,25 +1432,13 @@ p, li { white-space: pre-wrap; }
|
|||
</action>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>tabWidget</tabstop>
|
||||
<tabstop>tableWidgetInbox</tabstop>
|
||||
<tabstop>textEditInboxMessage</tabstop>
|
||||
<tabstop>radioButtonSpecific</tabstop>
|
||||
<tabstop>radioButtonBroadcast</tabstop>
|
||||
<tabstop>comboBoxSendFrom</tabstop>
|
||||
<tabstop>lineEditTo</tabstop>
|
||||
<tabstop>pushButtonLoadFromAddressBook</tabstop>
|
||||
<tabstop>lineEditSubject</tabstop>
|
||||
<tabstop>textEditMessage</tabstop>
|
||||
<tabstop>pushButtonSend</tabstop>
|
||||
<tabstop>tableWidgetSent</tabstop>
|
||||
<tabstop>textEditSentMessage</tabstop>
|
||||
<tabstop>pushButtonNewAddress</tabstop>
|
||||
<tabstop>tableWidgetYourIdentities</tabstop>
|
||||
<tabstop>pushButtonAddSubscription</tabstop>
|
||||
<tabstop>tableWidgetSubscriptions</tabstop>
|
||||
<tabstop>pushButtonAddAddressBook</tabstop>
|
||||
<tabstop>tableWidgetAddressBook</tabstop>
|
||||
<tabstop>radioButtonBlacklist</tabstop>
|
||||
<tabstop>radioButtonWhitelist</tabstop>
|
||||
<tabstop>pushButtonAddBlacklist</tabstop>
|
||||
|
@ -1348,54 +1449,5 @@ p, li { white-space: pre-wrap; }
|
|||
<resources>
|
||||
<include location="bitmessage_icons.qrc"/>
|
||||
</resources>
|
||||
<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>
|
||||
<connections/>
|
||||
</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.setItemText(4, _fromUtf8("Deutsch"))
|
||||
self.languageComboBox.addItem(_fromUtf8(""))
|
||||
self.languageComboBox.setItemText(5, _fromUtf8("Españl"))
|
||||
self.languageComboBox.setItemText(5, _fromUtf8("Español"))
|
||||
self.languageComboBox.addItem(_fromUtf8(""))
|
||||
self.languageComboBox.setItemText(6, _fromUtf8("русский"))
|
||||
self.languageComboBox.addItem(_fromUtf8(""))
|
||||
|
@ -120,15 +120,21 @@ class Ui_settingsDialog(object):
|
|||
self.groupBox1.setObjectName(_fromUtf8("groupBox1"))
|
||||
self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1)
|
||||
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
|
||||
spacerItem = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.gridLayout_3.addItem(spacerItem, 0, 0, 1, 1)
|
||||
#spacerItem = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
#self.gridLayout_3.addItem(spacerItem, 0, 0, 1, 1)
|
||||
self.label = QtGui.QLabel(self.groupBox1)
|
||||
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.setMaximumSize(QtCore.QSize(70, 16777215))
|
||||
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.groupBox_3 = QtGui.QGroupBox(self.tabNetworkSettings)
|
||||
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)
|
||||
spacerItem8 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
|
||||
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.tabNamecoin = QtGui.QWidget()
|
||||
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.groupBox1.setTitle(_translate("settingsDialog", "Listening 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.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))
|
||||
|
@ -474,6 +487,7 @@ class Ui_settingsDialog(object):
|
|||
self.label_13.setText(_translate("settingsDialog", "Maximum acceptable total 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.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_17.setText(_translate("settingsDialog", "Host:", None))
|
||||
self.label_18.setText(_translate("settingsDialog", "Port:", None))
|
||||
|
|
|
@ -153,7 +153,7 @@
|
|||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true" comment="es">Españl</string>
|
||||
<string notr="true" comment="es">Español</string>
|
||||
</property>
|
||||
</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
|
||||
|
||||
name = "Bitmessage"
|
||||
version = "0.4.4"
|
||||
version = os.getenv("PYBITMESSAGEVERSION", "custom")
|
||||
mainscript = ["bitmessagemain.py"]
|
||||
|
||||
setup(
|
||||
|
@ -11,7 +12,7 @@ setup(
|
|||
setup_requires = ["py2app"],
|
||||
options = dict(
|
||||
py2app = dict(
|
||||
resources = ["images", "translations"],
|
||||
resources = ["images", "translations", "bitmsghash", "sslkeys"],
|
||||
includes = ['sip', 'PyQt4._qt'],
|
||||
iconfile = "images/bitmessage.icns"
|
||||
)
|
||||
|
|
|
@ -8,17 +8,26 @@ import hashlib
|
|||
import highlevelcrypto
|
||||
from addresses import *
|
||||
from debug import logger
|
||||
from helper_threading import *
|
||||
from pyelliptic import arithmetic
|
||||
import tr
|
||||
|
||||
class addressGenerator(threading.Thread):
|
||||
class addressGenerator(threading.Thread, StoppableThread):
|
||||
|
||||
def __init__(self):
|
||||
# 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):
|
||||
while True:
|
||||
while shared.shutdown == 0:
|
||||
queueValue = shared.addressGeneratorQueue.get()
|
||||
nonceTrialsPerByte = 0
|
||||
payloadLengthExtraBytes = 0
|
||||
|
@ -54,6 +63,8 @@ class addressGenerator(threading.Thread):
|
|||
numberOfNullBytesDemandedOnFrontOfRipeHash = 2
|
||||
else:
|
||||
numberOfNullBytesDemandedOnFrontOfRipeHash = 1 # the default
|
||||
elif queueValue[0] == 'stopThread':
|
||||
break
|
||||
else:
|
||||
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))
|
||||
|
@ -150,10 +161,8 @@ class addressGenerator(threading.Thread):
|
|||
sys.stderr.write(
|
||||
'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.')
|
||||
if command == 'createDeterministicAddresses':
|
||||
statusbar = 'Generating ' + str(
|
||||
numberOfAddressesToMake) + ' new addresses.'
|
||||
shared.UISignalQueue.put((
|
||||
'updateStatusBar', statusbar))
|
||||
'updateStatusBar', tr.translateText("MainWindow","Generating %1 new addresses.").arg(str(numberOfAddressesToMake))))
|
||||
signingKeyNonce = 0
|
||||
encryptionKeyNonce = 1
|
||||
listOfNewAddressesToSendOutThroughTheAPI = [
|
||||
|
@ -278,4 +287,4 @@ class addressGenerator(threading.Thread):
|
|||
else:
|
||||
raise Exception(
|
||||
"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.
|
||||
"""
|
||||
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
|
||||
closed it before it finished processing everything in the
|
||||
|
@ -741,8 +741,7 @@ class objectProcessor(threading.Thread):
|
|||
|
||||
fromAddress = encodeAddress(
|
||||
sendersAddressVersion, sendersStream, calculatedRipe)
|
||||
with shared.printLock:
|
||||
print 'fromAddress:', fromAddress
|
||||
logger.debug('fromAddress: ' + fromAddress)
|
||||
|
||||
if messageEncodingType == 2:
|
||||
subject, body = self.decodeType2Message(message)
|
||||
|
|
|
@ -2,6 +2,7 @@ import threading
|
|||
import time
|
||||
import random
|
||||
import shared
|
||||
import select
|
||||
import socks
|
||||
import socket
|
||||
import sys
|
||||
|
@ -9,14 +10,16 @@ import tr
|
|||
|
||||
from class_sendDataThread import *
|
||||
from class_receiveDataThread import *
|
||||
from helper_threading import *
|
||||
|
||||
# For each stream to which we connect, several outgoingSynSender threads
|
||||
# will exist and will collectively create 8 connections with peers.
|
||||
|
||||
class outgoingSynSender(threading.Thread):
|
||||
class outgoingSynSender(threading.Thread, StoppableThread):
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
threading.Thread.__init__(self, name="outgoingSynSender")
|
||||
self.initStop()
|
||||
|
||||
def setup(self, streamNumber, selfInitiatedConnections):
|
||||
self.streamNumber = streamNumber
|
||||
|
@ -36,13 +39,21 @@ class outgoingSynSender(threading.Thread):
|
|||
|
||||
return peer
|
||||
|
||||
def stopThread(self):
|
||||
super(outgoingSynSender, self).stopThread()
|
||||
try:
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
except:
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
|
||||
time.sleep(2)
|
||||
while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'):
|
||||
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect') and not self._stopped:
|
||||
self.stop.wait(2)
|
||||
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
|
||||
while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections:
|
||||
time.sleep(10)
|
||||
self.stop.wait(10)
|
||||
if shared.shutdown:
|
||||
break
|
||||
random.seed()
|
||||
|
@ -53,7 +64,9 @@ class outgoingSynSender(threading.Thread):
|
|||
# print 'choosing new sample'
|
||||
random.seed()
|
||||
peer = self._getPeer()
|
||||
time.sleep(1)
|
||||
self.stop.wait(1)
|
||||
if shared.shutdown:
|
||||
break
|
||||
# Clear out the shared.alreadyAttemptedConnectionsList every half
|
||||
# hour so that this program will again attempt a connection
|
||||
# to any nodes, even ones it has already tried.
|
||||
|
@ -63,13 +76,17 @@ class outgoingSynSender(threading.Thread):
|
|||
time.time())
|
||||
shared.alreadyAttemptedConnectionsListLock.acquire()
|
||||
shared.alreadyAttemptedConnectionsList[peer] = 0
|
||||
try:
|
||||
shared.alreadyAttemptedConnectionsListLock.release()
|
||||
except threading.ThreadError as e:
|
||||
pass
|
||||
self.name = "outgoingSynSender-" + peer.host
|
||||
if peer.host.find(':') == -1:
|
||||
address_family = socket.AF_INET
|
||||
else:
|
||||
address_family = socket.AF_INET6
|
||||
try:
|
||||
sock = socks.socksocket(address_family, socket.SOCK_STREAM)
|
||||
self.sock = socks.socksocket(address_family, socket.SOCK_STREAM)
|
||||
except:
|
||||
"""
|
||||
The line can fail on Windows systems which aren't
|
||||
|
@ -86,22 +103,19 @@ class outgoingSynSender(threading.Thread):
|
|||
except:
|
||||
pass
|
||||
shared.knownNodesLock.release()
|
||||
with shared.printLock:
|
||||
print 'deleting ', peer, 'from shared.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible.'
|
||||
logger.debug('deleting ' + str(peer) + ' from shared.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible.')
|
||||
continue
|
||||
# This option apparently avoids the TIME_WAIT state so that we
|
||||
# can rebind faster
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.settimeout(20)
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.sock.settimeout(20)
|
||||
if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2:
|
||||
with shared.printLock:
|
||||
print 'Trying an outgoing connection to', peer
|
||||
logger.debug('Trying an outgoing connection to ' + str(peer))
|
||||
|
||||
# sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS4a':
|
||||
if shared.verbose >= 2:
|
||||
with shared.printLock:
|
||||
print '(Using SOCKS4a) Trying an outgoing connection to', peer
|
||||
logger.debug ('(Using SOCKS4a) Trying an outgoing connection to ' + str(peer))
|
||||
|
||||
proxytype = socks.PROXY_TYPE_SOCKS4
|
||||
sockshostname = shared.config.get(
|
||||
|
@ -114,15 +128,14 @@ class outgoingSynSender(threading.Thread):
|
|||
'bitmessagesettings', 'socksusername')
|
||||
sockspassword = shared.config.get(
|
||||
'bitmessagesettings', 'sockspassword')
|
||||
sock.setproxy(
|
||||
self.sock.setproxy(
|
||||
proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
|
||||
else:
|
||||
sock.setproxy(
|
||||
self.sock.setproxy(
|
||||
proxytype, sockshostname, socksport, rdns)
|
||||
elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
|
||||
if shared.verbose >= 2:
|
||||
with shared.printLock:
|
||||
print '(Using SOCKS5) Trying an outgoing connection to', peer
|
||||
logger.debug ('(Using SOCKS5) Trying an outgoing connection to ' + str(peer))
|
||||
|
||||
proxytype = socks.PROXY_TYPE_SOCKS5
|
||||
sockshostname = shared.config.get(
|
||||
|
@ -135,19 +148,19 @@ class outgoingSynSender(threading.Thread):
|
|||
'bitmessagesettings', 'socksusername')
|
||||
sockspassword = shared.config.get(
|
||||
'bitmessagesettings', 'sockspassword')
|
||||
sock.setproxy(
|
||||
self.sock.setproxy(
|
||||
proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
|
||||
else:
|
||||
sock.setproxy(
|
||||
self.sock.setproxy(
|
||||
proxytype, sockshostname, socksport, rdns)
|
||||
|
||||
try:
|
||||
sock.connect((peer.host, peer.port))
|
||||
self.sock.connect((peer.host, peer.port))
|
||||
rd = receiveDataThread()
|
||||
rd.daemon = True # close the main program even if there are threads left
|
||||
someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory.
|
||||
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.port,
|
||||
self.streamNumber,
|
||||
|
@ -155,20 +168,18 @@ class outgoingSynSender(threading.Thread):
|
|||
self.selfInitiatedConnections,
|
||||
sendDataThreadQueue)
|
||||
rd.start()
|
||||
with shared.printLock:
|
||||
print self, 'connected to', peer, 'during an outgoing attempt.'
|
||||
logger.debug(str(self) + ' connected to ' + str(peer) + ' during an outgoing attempt.')
|
||||
|
||||
|
||||
sd = sendDataThread(sendDataThreadQueue)
|
||||
sd.setup(sock, peer.host, peer.port, self.streamNumber,
|
||||
sd.setup(self.sock, peer.host, peer.port, self.streamNumber,
|
||||
someObjectsOfWhichThisRemoteNodeIsAlreadyAware)
|
||||
sd.start()
|
||||
sd.sendVersionMessage()
|
||||
|
||||
except socks.GeneralProxyError as err:
|
||||
if shared.verbose >= 2:
|
||||
with shared.printLock:
|
||||
print 'Could NOT connect to', peer, 'during outgoing attempt.', err
|
||||
logger.debug('Could NOT connect to ' + str(peer) + ' during outgoing attempt. ' + str(err))
|
||||
|
||||
deletedPeer = None
|
||||
with shared.knownNodesLock:
|
||||
|
@ -186,8 +197,7 @@ class outgoingSynSender(threading.Thread):
|
|||
del shared.knownNodes[self.streamNumber][peer]
|
||||
deletedPeer = peer
|
||||
if deletedPeer:
|
||||
with shared.printLock:
|
||||
print 'deleting', peer, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.'
|
||||
str ('deleting ' + str(peer) + ' from shared.knownNodes because it is more than 48 hours old and we could not connect to it.')
|
||||
|
||||
except socks.Socks5AuthError as err:
|
||||
shared.UISignalQueue.put((
|
||||
|
@ -195,16 +205,15 @@ class outgoingSynSender(threading.Thread):
|
|||
"MainWindow", "SOCKS5 Authentication problem: %1").arg(str(err))))
|
||||
except socks.Socks5Error as err:
|
||||
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:
|
||||
print 'Socks4Error:', err
|
||||
logger.error('Socks4Error: ' + str(err))
|
||||
except socket.error as err:
|
||||
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:
|
||||
if shared.verbose >= 1:
|
||||
with shared.printLock:
|
||||
print 'Could NOT connect to', peer, 'during outgoing attempt.', err
|
||||
logger.debug('Could NOT connect to ' + str(peer) + 'during outgoing attempt. ' + str(err))
|
||||
|
||||
deletedPeer = None
|
||||
with shared.knownNodesLock:
|
||||
|
@ -222,12 +231,9 @@ class outgoingSynSender(threading.Thread):
|
|||
del shared.knownNodes[self.streamNumber][peer]
|
||||
deletedPeer = peer
|
||||
if deletedPeer:
|
||||
with shared.printLock:
|
||||
print 'deleting', peer, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.'
|
||||
logger.debug('deleting ' + str(peer) + ' from shared.knownNodes because it is more than 48 hours old and we could not connect to it.')
|
||||
|
||||
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
|
||||
traceback.print_exc()
|
||||
time.sleep(0.1)
|
||||
logger.exception('An exception has occurred in the outgoingSynSender thread that was not caught by other exception types:')
|
||||
self.stop.wait(0.1)
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
doTimingAttackMitigation = True
|
||||
|
||||
import errno
|
||||
import time
|
||||
import threading
|
||||
import shared
|
||||
import hashlib
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import random
|
||||
import ssl
|
||||
from struct import unpack, pack
|
||||
import sys
|
||||
import traceback
|
||||
|
@ -25,7 +29,7 @@ from debug import logger
|
|||
class receiveDataThread(threading.Thread):
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
threading.Thread.__init__(self, name="receiveData")
|
||||
self.data = ''
|
||||
self.verackSent = False
|
||||
self.verackReceived = False
|
||||
|
@ -42,6 +46,7 @@ class receiveDataThread(threading.Thread):
|
|||
|
||||
self.sock = sock
|
||||
self.peer = shared.Peer(HOST, port)
|
||||
self.name = "receiveData-" + self.peer.host
|
||||
self.streamNumber = streamNumber
|
||||
self.objectsThatWeHaveYetToGetFromThisPeer = {}
|
||||
self.selfInitiatedConnections = selfInitiatedConnections
|
||||
|
@ -49,6 +54,7 @@ class receiveDataThread(threading.Thread):
|
|||
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.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.
|
||||
self.initiatedConnection = False
|
||||
else:
|
||||
|
@ -57,8 +63,7 @@ class receiveDataThread(threading.Thread):
|
|||
self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware
|
||||
|
||||
def run(self):
|
||||
with shared.printLock:
|
||||
print 'receiveDataThread starting. ID', str(id(self)) + '. The size of the shared.connectedHostsList is now', len(shared.connectedHostsList)
|
||||
logger.debug('receiveDataThread starting. ID ' + str(id(self)) + '. The size of the shared.connectedHostsList is now ' + str(len(shared.connectedHostsList)))
|
||||
|
||||
while True:
|
||||
if shared.config.getint('bitmessagesettings', 'maxdownloadrate') == 0:
|
||||
|
@ -76,38 +81,41 @@ class receiveDataThread(threading.Thread):
|
|||
shared.numberOfBytesReceivedLastSecond = 0
|
||||
dataLen = len(self.data)
|
||||
try:
|
||||
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
|
||||
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
|
||||
except socket.timeout:
|
||||
with shared.printLock:
|
||||
print 'Timeout occurred waiting for data from', self.peer, '. Closing receiveData thread. (ID:', str(id(self)) + ')'
|
||||
logger.error ('Timeout occurred waiting for data from ' + str(self.peer) + '. Closing receiveData thread. (ID: ' + str(id(self)) + ')')
|
||||
break
|
||||
except Exception as err:
|
||||
with shared.printLock:
|
||||
print 'sock.recv error. Closing receiveData thread (' + str(self.peer) + ', Thread ID:', str(id(self)) + ').', err
|
||||
if (sys.platform == 'win32' and err.errno in ([2, 10035])) or (sys.platform != 'win32' and err.errno == errno.EWOULDBLOCK):
|
||||
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
|
||||
# print 'Received', repr(self.data)
|
||||
if len(self.data) == dataLen: # If self.sock.recv returned no data:
|
||||
with shared.printLock:
|
||||
print 'Connection to', self.peer, 'closed. Closing receiveData thread. (ID:', str(id(self)) + ')'
|
||||
logger.debug('Connection to ' + str(self.peer) + ' closed. Closing receiveData thread. (ID: ' + str(id(self)) + ')')
|
||||
break
|
||||
else:
|
||||
self.processData()
|
||||
|
||||
try:
|
||||
del self.selfInitiatedConnections[self.streamNumber][self]
|
||||
with shared.printLock:
|
||||
print 'removed self (a receiveDataThread) from selfInitiatedConnections'
|
||||
logger.debug('removed self (a receiveDataThread) from selfInitiatedConnections')
|
||||
except:
|
||||
pass
|
||||
self.sendDataThreadQueue.put((0, 'shutdown','no data')) # commands the corresponding sendDataThread to shut itself down.
|
||||
try:
|
||||
del shared.connectedHostsList[self.peer.host]
|
||||
except Exception as err:
|
||||
with shared.printLock:
|
||||
print 'Could not delete', self.peer.host, 'from shared.connectedHostsList.', err
|
||||
logger.error('Could not delete ' + str(self.peer.host) + ' from shared.connectedHostsList.' + str(err))
|
||||
|
||||
try:
|
||||
del shared.numberOfObjectsThatWeHaveYetToGetPerPeer[
|
||||
|
@ -115,8 +123,7 @@ class receiveDataThread(threading.Thread):
|
|||
except:
|
||||
pass
|
||||
shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data'))
|
||||
with shared.printLock:
|
||||
print 'receiveDataThread ending. ID', str(id(self)) + '. The size of the shared.connectedHostsList is now', len(shared.connectedHostsList)
|
||||
logger.debug('receiveDataThread ending. ID ' + str(id(self)) + '. The size of the shared.connectedHostsList is now ' + str(len(shared.connectedHostsList)))
|
||||
|
||||
|
||||
def processData(self):
|
||||
|
@ -137,7 +144,7 @@ class receiveDataThread(threading.Thread):
|
|||
return
|
||||
payload = self.data[shared.Header.size:payloadLength + shared.Header.size]
|
||||
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:]
|
||||
del magic,command,payloadLength,checksum,payload # better to clean up before the recursive call
|
||||
self.processData()
|
||||
|
@ -152,8 +159,7 @@ class receiveDataThread(threading.Thread):
|
|||
|
||||
#Strip the nulls
|
||||
command = command.rstrip('\x00')
|
||||
with shared.printLock:
|
||||
print 'remoteCommand', repr(command), ' from', self.peer
|
||||
logger.debug('remoteCommand ' + repr(command) + ' from ' + str(self.peer))
|
||||
|
||||
try:
|
||||
#TODO: Use a dispatcher here
|
||||
|
@ -191,14 +197,12 @@ class receiveDataThread(threading.Thread):
|
|||
objectHash, = random.sample(
|
||||
self.objectsThatWeHaveYetToGetFromThisPeer, 1)
|
||||
if objectHash in shared.inventory:
|
||||
with shared.printLock:
|
||||
print 'Inventory (in memory) already has object listed in inv message.'
|
||||
logger.debug('Inventory (in memory) already has object listed in inv message.')
|
||||
del self.objectsThatWeHaveYetToGetFromThisPeer[
|
||||
objectHash]
|
||||
elif shared.isInSqlInventory(objectHash):
|
||||
if shared.verbose >= 3:
|
||||
with shared.printLock:
|
||||
print 'Inventory (SQL on disk) already has object listed in inv message.'
|
||||
logger.debug('Inventory (SQL on disk) already has object listed in inv message.')
|
||||
del self.objectsThatWeHaveYetToGetFromThisPeer[
|
||||
objectHash]
|
||||
else:
|
||||
|
@ -207,8 +211,7 @@ class receiveDataThread(threading.Thread):
|
|||
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.
|
||||
if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0:
|
||||
with shared.printLock:
|
||||
print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now 0'
|
||||
logger.debug('(concerning' + str(self.peer) + ') number of objectsThatWeHaveYetToGetFromThisPeer is now 0')
|
||||
try:
|
||||
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.
|
||||
|
@ -217,16 +220,14 @@ class receiveDataThread(threading.Thread):
|
|||
break
|
||||
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.
|
||||
with shared.printLock:
|
||||
print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now 0'
|
||||
logger.debug('(concerning' + str(self.peer) + ') number of objectsThatWeHaveYetToGetFromThisPeer is now 0')
|
||||
try:
|
||||
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.
|
||||
except:
|
||||
pass
|
||||
if len(self.objectsThatWeHaveYetToGetFromThisPeer) > 0:
|
||||
with shared.printLock:
|
||||
print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer)
|
||||
logger.debug('(concerning' + str(self.peer) + ') number of objectsThatWeHaveYetToGetFromThisPeer is now ' + str(len(self.objectsThatWeHaveYetToGetFromThisPeer)))
|
||||
|
||||
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.
|
||||
|
@ -234,14 +235,12 @@ class receiveDataThread(threading.Thread):
|
|||
|
||||
|
||||
def sendpong(self):
|
||||
with shared.printLock:
|
||||
print 'Sending pong'
|
||||
logger.debug('Sending pong')
|
||||
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('pong')))
|
||||
|
||||
|
||||
def recverack(self):
|
||||
with shared.printLock:
|
||||
print 'verack received'
|
||||
logger.debug('verack received')
|
||||
self.verackReceived = True
|
||||
if self.verackSent:
|
||||
# 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
|
||||
return
|
||||
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
|
||||
self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', 'no data'))
|
||||
self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', (self.services, self.sslSock)))
|
||||
|
||||
if not self.initiatedConnection:
|
||||
shared.clientHasReceivedIncomingConnections = True
|
||||
shared.UISignalQueue.put(('setStatusIcon', 'green'))
|
||||
self.sock.settimeout(
|
||||
600) # We'll send out a pong every 5 minutes to make sure the connection stays alive if there has been no other traffic to send lately.
|
||||
shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data'))
|
||||
with shared.printLock:
|
||||
print 'Connection fully established with', self.peer
|
||||
print 'The size of the connectedHostsList is now', len(shared.connectedHostsList)
|
||||
print 'The length of sendDataQueues is now:', len(shared.sendDataQueues)
|
||||
print 'broadcasting addr from within connectionFullyEstablished function.'
|
||||
logger.debug('Connection fully established with ' + str(self.peer) + "\n" + \
|
||||
'The size of the connectedHostsList is now ' + str(len(shared.connectedHostsList)) + "\n" + \
|
||||
'The length of sendDataQueues is now: ' + str(len(shared.sendDataQueues)) + "\n" + \
|
||||
'broadcasting addr from within connectionFullyEstablished function.')
|
||||
|
||||
# Let all of our peers know about this new node.
|
||||
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.
|
||||
if not self.initiatedConnection and len(shared.connectedHostsList) > 200:
|
||||
with shared.printLock:
|
||||
print 'We are connected to too many people. Closing connection.'
|
||||
logger.info ('We are connected to too many people. Closing connection.')
|
||||
|
||||
self.sendDataThreadQueue.put((0, 'shutdown','no data'))
|
||||
return
|
||||
|
@ -320,8 +337,7 @@ class receiveDataThread(threading.Thread):
|
|||
# function for broadcasting invs to everyone in our stream.
|
||||
def sendinvMessageToJustThisOnePeer(self, numberOfObjects, payload):
|
||||
payload = encodeVarint(numberOfObjects) + payload
|
||||
with shared.printLock:
|
||||
print 'Sending huge inv message with', numberOfObjects, 'objects to just this one peer'
|
||||
logger.debug('Sending huge inv message with ' + str(numberOfObjects) + ' objects to just this one peer')
|
||||
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('inv', payload)))
|
||||
|
||||
def _sleepForTimingAttackMitigation(self, sleepTime):
|
||||
|
@ -329,8 +345,7 @@ class receiveDataThread(threading.Thread):
|
|||
# only connected to the trusted peer because we can trust the
|
||||
# peer not to attack
|
||||
if sleepTime > 0 and doTimingAttackMitigation and shared.trustedPeer == None:
|
||||
with shared.printLock:
|
||||
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
|
||||
logger.debug('Timing attack mitigation: Sleeping for ' + str(sleepTime) + ' seconds.')
|
||||
time.sleep(sleepTime)
|
||||
|
||||
def recerror(self, data):
|
||||
|
@ -388,30 +403,27 @@ class receiveDataThread(threading.Thread):
|
|||
if len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer) > 0:
|
||||
for key, value in shared.numberOfObjectsThatWeHaveYetToGetPerPeer.items():
|
||||
totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers += value
|
||||
with shared.printLock:
|
||||
print 'number of keys(hosts) in shared.numberOfObjectsThatWeHaveYetToGetPerPeer:', len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)
|
||||
print 'totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers = ', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers
|
||||
logger.debug('number of keys(hosts) in shared.numberOfObjectsThatWeHaveYetToGetPerPeer: ' + str(len(shared.numberOfObjectsThatWeHaveYetToGetPerPeer)) + "\n" + \
|
||||
'totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers = ' + str(totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers))
|
||||
|
||||
numberOfItemsInInv, lengthOfVarint = decodeVarint(data[:10])
|
||||
if numberOfItemsInInv > 50000:
|
||||
sys.stderr.write('Too many items in inv message!')
|
||||
return
|
||||
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
|
||||
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
|
||||
with shared.printLock:
|
||||
print 'We already have', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers, 'items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.'
|
||||
if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000 and shared.trustedPeer == None: # inv flooding attack mitigation
|
||||
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.')
|
||||
return
|
||||
self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[
|
||||
data[lengthOfVarint:32 + lengthOfVarint]] = 0
|
||||
shared.numberOfInventoryLookupsPerformed += 1
|
||||
if data[lengthOfVarint:32 + lengthOfVarint] in shared.inventory:
|
||||
with shared.printLock:
|
||||
print 'Inventory (in memory) has inventory item already.'
|
||||
logger.debug('Inventory (in memory) has inventory item already.')
|
||||
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:
|
||||
self.sendgetdata(data[lengthOfVarint:32 + lengthOfVarint])
|
||||
else:
|
||||
|
@ -425,9 +437,8 @@ class receiveDataThread(threading.Thread):
|
|||
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)
|
||||
for item in objectsNewToMe:
|
||||
if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation
|
||||
with shared.printLock:
|
||||
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.'
|
||||
if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000 and shared.trustedPeer == None: # inv flooding attack mitigation
|
||||
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.')
|
||||
break
|
||||
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.
|
||||
|
@ -438,8 +449,7 @@ class receiveDataThread(threading.Thread):
|
|||
# Send a getdata message to our peer to request the object with the given
|
||||
# hash
|
||||
def sendgetdata(self, hash):
|
||||
with shared.printLock:
|
||||
print 'sending getdata to retrieve object with hash:', hash.encode('hex')
|
||||
logger.debug('sending getdata to retrieve object with hash: ' + hash.encode('hex'))
|
||||
payload = '\x01' + hash
|
||||
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('getdata', payload)))
|
||||
|
||||
|
@ -449,13 +459,12 @@ class receiveDataThread(threading.Thread):
|
|||
numberOfRequestedInventoryItems, lengthOfVarint = decodeVarint(
|
||||
data[:10])
|
||||
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
|
||||
for i in xrange(numberOfRequestedInventoryItems):
|
||||
hash = data[lengthOfVarint + (
|
||||
i * 32):32 + lengthOfVarint + (i * 32)]
|
||||
with shared.printLock:
|
||||
print 'received getdata request for item:', hash.encode('hex')
|
||||
logger.debug('received getdata request for item:' + hash.encode('hex'))
|
||||
|
||||
shared.numberOfInventoryLookupsPerformed += 1
|
||||
shared.inventoryLock.acquire()
|
||||
|
@ -478,33 +487,35 @@ class receiveDataThread(threading.Thread):
|
|||
|
||||
# Our peer has requested (in a getdata message) that we send an object.
|
||||
def sendObject(self, payload):
|
||||
with shared.printLock:
|
||||
print 'sending an object.'
|
||||
logger.debug('sending an object.')
|
||||
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('object',payload)))
|
||||
|
||||
|
||||
def _checkIPv4Address(self, host, hostStandardFormat):
|
||||
# print 'hostStandardFormat', hostStandardFormat
|
||||
if host[0] == '\x7F':
|
||||
print 'Ignoring IP address in loopback range:', hostStandardFormat
|
||||
if host[0] == '\x7F': # 127/8
|
||||
logger.debug('Ignoring IP address in loopback range: ' + hostStandardFormat)
|
||||
return False
|
||||
if host[0] == '\x0A':
|
||||
print 'Ignoring IP address in private range:', hostStandardFormat
|
||||
if host[0] == '\x0A': # 10/8
|
||||
logger.debug('Ignoring IP address in private range: ' + hostStandardFormat)
|
||||
return False
|
||||
if host[0:2] == '\xC0\xA8':
|
||||
print 'Ignoring IP address in private range:', hostStandardFormat
|
||||
if host[0:2] == '\xC0\xA8': # 192.168/16
|
||||
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 True
|
||||
|
||||
def _checkIPv6Address(self, host, hostStandardFormat):
|
||||
if host == ('\x00' * 15) + '\x01':
|
||||
print 'Ignoring loopback address:', hostStandardFormat
|
||||
logger.debug('Ignoring loopback address: ' + hostStandardFormat)
|
||||
return False
|
||||
if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80:
|
||||
print 'Ignoring local address:', hostStandardFormat
|
||||
logger.debug ('Ignoring local address: ' + hostStandardFormat)
|
||||
return False
|
||||
if (ord(host[0]) & 0xfe) == 0xfc:
|
||||
print 'Ignoring unique local address:', hostStandardFormat
|
||||
logger.debug ('Ignoring unique local address: ' + hostStandardFormat)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
@ -514,13 +525,12 @@ class receiveDataThread(threading.Thread):
|
|||
data[:10])
|
||||
|
||||
if shared.verbose >= 1:
|
||||
with shared.printLock:
|
||||
print 'addr message contains', numberOfAddressesIncluded, 'IP addresses.'
|
||||
logger.debug('addr message contains ' + str(numberOfAddressesIncluded) + ' IP addresses.')
|
||||
|
||||
if numberOfAddressesIncluded > 1000 or numberOfAddressesIncluded == 0:
|
||||
return
|
||||
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
|
||||
|
||||
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.
|
||||
with shared.knownNodesLock:
|
||||
shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode
|
||||
with shared.printLock:
|
||||
print 'added new node', peerFromAddrMessage, 'to knownNodes in stream', recaddrStream
|
||||
logger.debug('added new node ' + str(peerFromAddrMessage) + ' to knownNodes in stream ' + str(recaddrStream))
|
||||
|
||||
shared.needToWriteKnownNodesToDisk = True
|
||||
hostDetails = (
|
||||
|
@ -574,8 +583,7 @@ class receiveDataThread(threading.Thread):
|
|||
with shared.knownNodesLock:
|
||||
shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode
|
||||
|
||||
with shared.printLock:
|
||||
print 'knownNodes currently has', len(shared.knownNodes[self.streamNumber]), 'nodes for this stream.'
|
||||
logger.debug('knownNodes currently has ' + str(len(shared.knownNodes[self.streamNumber])) + ' nodes for this stream.')
|
||||
|
||||
|
||||
# Send a huge addr message to our peer. This is only used
|
||||
|
@ -671,10 +679,10 @@ class receiveDataThread(threading.Thread):
|
|||
"""
|
||||
return
|
||||
self.remoteProtocolVersion, = unpack('>L', data[:4])
|
||||
self.services, = unpack('>q', data[4:12])
|
||||
if self.remoteProtocolVersion < 3:
|
||||
self.sendDataThreadQueue.put((0, 'shutdown','no data'))
|
||||
with shared.printLock:
|
||||
print 'Closing connection to old protocol version', self.remoteProtocolVersion, 'node: ', self.peer
|
||||
logger.debug ('Closing connection to old protocol version ' + str(self.remoteProtocolVersion) + ' node: ' + str(self.peer))
|
||||
return
|
||||
timestamp, = unpack('>Q', data[12:20])
|
||||
timeOffset = timestamp - int(time.time())
|
||||
|
@ -698,19 +706,28 @@ class receiveDataThread(threading.Thread):
|
|||
data[80:84])
|
||||
readPosition = 80 + lengthOfUseragentVarint
|
||||
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
|
||||
numberOfStreamsInVersionMessage, lengthOfNumberOfStreamsInVersionMessage = decodeVarint(
|
||||
data[readPosition:])
|
||||
readPosition += lengthOfNumberOfStreamsInVersionMessage
|
||||
self.streamNumber, lengthOfRemoteStreamNumber = decodeVarint(
|
||||
data[readPosition:])
|
||||
with shared.printLock:
|
||||
print 'Remote node useragent:', useragent, ' stream number:', self.streamNumber, ' time offset:', timeOffset, 'seconds.'
|
||||
logger.debug('Remote node useragent: ' + useragent + ' stream number:' + str(self.streamNumber) + ' time offset: ' + str(timeOffset) + ' seconds.')
|
||||
|
||||
if self.streamNumber != 1:
|
||||
self.sendDataThreadQueue.put((0, 'shutdown','no data'))
|
||||
with shared.printLock:
|
||||
print 'Closed connection to', self.peer, 'because they are interested in stream', self.streamNumber, '.'
|
||||
logger.debug ('Closed connection to ' + str(self.peer) + ' because they are interested in stream ' + str(self.streamNumber) + '.')
|
||||
return
|
||||
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.
|
||||
|
@ -720,8 +737,7 @@ class receiveDataThread(threading.Thread):
|
|||
self.sendDataThreadQueue.put((0, 'setStreamNumber', self.streamNumber))
|
||||
if data[72:80] == shared.eightBytesOfRandomDataUsedToDetectConnectionsToSelf:
|
||||
self.sendDataThreadQueue.put((0, 'shutdown','no data'))
|
||||
with shared.printLock:
|
||||
print 'Closing connection to myself: ', self.peer
|
||||
logger.debug('Closing connection to myself: ' + str(self.peer))
|
||||
return
|
||||
|
||||
# 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
|
||||
def sendversion(self):
|
||||
with shared.printLock:
|
||||
print 'Sending version message'
|
||||
logger.debug('Sending version message')
|
||||
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
|
||||
def sendverack(self):
|
||||
with shared.printLock:
|
||||
print 'Sending verack'
|
||||
logger.debug('Sending verack')
|
||||
self.sendDataThreadQueue.put((0, 'sendRawData', shared.CreatePacket('verack')))
|
||||
self.verackSent = True
|
||||
if self.verackReceived:
|
||||
|
|
|
@ -11,13 +11,14 @@ import socket
|
|||
from helper_generic import addDataPadding
|
||||
from class_objectHashHolder import *
|
||||
from addresses import *
|
||||
from debug import logger
|
||||
|
||||
# Every connection to a peer has a sendDataThread (and also a
|
||||
# receiveDataThread).
|
||||
class sendDataThread(threading.Thread):
|
||||
|
||||
def __init__(self, sendDataThreadQueue):
|
||||
threading.Thread.__init__(self)
|
||||
threading.Thread.__init__(self, name="sendData")
|
||||
self.sendDataThreadQueue = sendDataThreadQueue
|
||||
shared.sendDataQueues.append(self.sendDataThreadQueue)
|
||||
self.data = ''
|
||||
|
@ -35,29 +36,33 @@ class sendDataThread(threading.Thread):
|
|||
someObjectsOfWhichThisRemoteNodeIsAlreadyAware):
|
||||
self.sock = sock
|
||||
self.peer = shared.Peer(HOST, PORT)
|
||||
self.name = "sendData-" + self.peer.host
|
||||
self.streamNumber = streamNumber
|
||||
self.services = 0
|
||||
self.initiatedConnection = False
|
||||
self.remoteProtocolVersion = - \
|
||||
1 # This must be set using setRemoteProtocolVersion command which is sent through the self.sendDataThreadQueue queue.
|
||||
self.lastTimeISentData = int(
|
||||
time.time()) # If this value increases beyond five minutes ago, we'll send a pong message to keep the connection alive.
|
||||
self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware
|
||||
with shared.printLock:
|
||||
print 'The streamNumber of this sendDataThread (ID:', str(id(self)) + ') at setup() is', self.streamNumber
|
||||
if self.streamNumber == -1: # This was an incoming connection.
|
||||
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):
|
||||
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:
|
||||
print 'Sending version packet: ', repr(datatosend)
|
||||
logger.debug('Sending version packet: ' + repr(datatosend))
|
||||
|
||||
try:
|
||||
self.sendBytes(datatosend)
|
||||
except Exception as err:
|
||||
# if not 'Bad file descriptor' in err:
|
||||
with shared.printLock:
|
||||
sys.stderr.write('sock.sendall error: %s\n' % err)
|
||||
logger.error('sock.sendall error: %s\n' % err)
|
||||
|
||||
self.versionSent = 1
|
||||
|
||||
|
@ -82,6 +87,11 @@ class sendDataThread(threading.Thread):
|
|||
uploadRateLimitBytes = 999999999 # float("inf") doesn't work
|
||||
else:
|
||||
uploadRateLimitBytes = shared.config.getint('bitmessagesettings', 'maxuploadrate') * 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.numberOfBytesSentLastSecond += amountSent
|
||||
|
@ -90,15 +100,13 @@ class sendDataThread(threading.Thread):
|
|||
|
||||
|
||||
def run(self):
|
||||
with shared.printLock:
|
||||
print 'sendDataThread starting. ID:', str(id(self))+'. Number of queues in sendDataQueues:', len(shared.sendDataQueues)
|
||||
logger.debug('sendDataThread starting. ID: ' + str(id(self)) + '. Number of queues in sendDataQueues: ' + str(len(shared.sendDataQueues)))
|
||||
while True:
|
||||
deststream, command, data = self.sendDataThreadQueue.get()
|
||||
|
||||
if deststream == self.streamNumber or deststream == 0:
|
||||
if command == 'shutdown':
|
||||
with shared.printLock:
|
||||
print 'sendDataThread (associated with', self.peer, ') ID:', id(self), 'shutting down now.'
|
||||
logger.debug('sendDataThread (associated with ' + str(self.peer) + ') ID: ' + str(id(self)) + ' shutting down now.')
|
||||
break
|
||||
# When you receive an incoming connection, a sendDataThread is
|
||||
# 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:
|
||||
elif command == 'setStreamNumber':
|
||||
self.streamNumber = data
|
||||
with shared.printLock:
|
||||
print 'setting the stream number in the sendData thread (ID:', id(self), ') to', self.streamNumber
|
||||
logger.debug('setting the stream number in the sendData thread (ID: ' + str(id(self)) + ') to ' + str(self.streamNumber))
|
||||
elif command == 'setRemoteProtocolVersion':
|
||||
specifiedRemoteProtocolVersion = data
|
||||
with shared.printLock:
|
||||
print 'setting the remote node\'s protocol version in the sendDataThread (ID:', id(self), ') to', specifiedRemoteProtocolVersion
|
||||
logger.debug('setting the remote node\'s protocol version in the sendDataThread (ID: ' + str(id(self)) + ') to ' + str(specifiedRemoteProtocolVersion))
|
||||
self.remoteProtocolVersion = specifiedRemoteProtocolVersion
|
||||
elif command == 'advertisepeer':
|
||||
self.objectHashHolderInstance.holdPeer(data)
|
||||
|
@ -136,8 +142,7 @@ class sendDataThread(threading.Thread):
|
|||
try:
|
||||
self.sendBytes(packet)
|
||||
except:
|
||||
with shared.printLock:
|
||||
print 'sendaddr: self.sock.sendall failed'
|
||||
logger.error('sendaddr: self.sock.sendall failed')
|
||||
break
|
||||
elif command == 'advertiseobject':
|
||||
self.objectHashHolderInstance.holdHash(data)
|
||||
|
@ -153,34 +158,30 @@ class sendDataThread(threading.Thread):
|
|||
try:
|
||||
self.sendBytes(packet)
|
||||
except:
|
||||
with shared.printLock:
|
||||
print 'sendinv: self.sock.sendall failed'
|
||||
logger.error('sendinv: self.sock.sendall failed')
|
||||
break
|
||||
elif command == 'pong':
|
||||
self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware.clear() # To save memory, let us clear this data structure from time to time. As its function is to help us keep from sending inv messages to peers which sent us the same inv message mere seconds earlier, it will be fine to clear this data structure from time to time.
|
||||
if self.lastTimeISentData < (int(time.time()) - 298):
|
||||
# Send out a pong message to keep the connection alive.
|
||||
with shared.printLock:
|
||||
print 'Sending pong to', self.peer, 'to keep connection alive.'
|
||||
logger.debug('Sending pong to ' + str(self.peer) + ' to keep connection alive.')
|
||||
packet = shared.CreatePacket('pong')
|
||||
try:
|
||||
self.sendBytes(packet)
|
||||
except:
|
||||
with shared.printLock:
|
||||
print 'send pong failed'
|
||||
logger.error('send pong failed')
|
||||
break
|
||||
elif command == 'sendRawData':
|
||||
try:
|
||||
self.sendBytes(data)
|
||||
except:
|
||||
with shared.printLock:
|
||||
print 'Sending of data to', self.peer, 'failed. sendDataThread thread', self, 'ending now.'
|
||||
logger.error('Sending of data to ' + str(self.peer) + ' failed. sendDataThread thread ' + str(self) + ' ending now.')
|
||||
break
|
||||
elif command == 'connectionIsOrWasFullyEstablished':
|
||||
self.connectionIsOrWasFullyEstablished = True
|
||||
self.services, self.sslSock = data
|
||||
else:
|
||||
with shared.printLock:
|
||||
print 'sendDataThread ID:', id(self), 'ignoring command', command, 'because the thread is not in stream', deststream
|
||||
logger.error('sendDataThread ID: ' + str(id(self)) + ' ignoring command ' + command + ' because the thread is not in stream' + str(deststream))
|
||||
|
||||
try:
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
|
@ -188,6 +189,5 @@ class sendDataThread(threading.Thread):
|
|||
except:
|
||||
pass
|
||||
shared.sendDataQueues.remove(self.sendDataThreadQueue)
|
||||
with shared.printLock:
|
||||
print 'sendDataThread ending. ID:', str(id(self))+'. Number of queues in sendDataQueues:', len(shared.sendDataQueues)
|
||||
logger.info('sendDataThread ending. ID: ' + str(id(self)) + '. Number of queues in sendDataQueues: ' + str(len(shared.sendDataQueues)))
|
||||
self.objectHashHolderInstance.close()
|
||||
|
|
|
@ -7,6 +7,7 @@ import pickle
|
|||
|
||||
import tr#anslate
|
||||
from helper_sql import *
|
||||
from helper_threading import *
|
||||
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):
|
||||
threading.Thread.__init__(self)
|
||||
threading.Thread.__init__(self, name="singleCleaner")
|
||||
self.initStop()
|
||||
|
||||
def run(self):
|
||||
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.
|
||||
shared.maximumLengthOfTimeToBotherResendingMessages = float('inf')
|
||||
|
||||
while True:
|
||||
while shared.shutdown == 0:
|
||||
shared.UISignalQueue.put((
|
||||
'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.
|
||||
|
@ -83,10 +85,8 @@ class singleCleaner(threading.Thread):
|
|||
int(time.time()) - shared.maximumLengthOfTimeToBotherResendingMessages)
|
||||
for row in queryreturn:
|
||||
if len(row) < 2:
|
||||
with shared.printLock:
|
||||
sys.stderr.write(
|
||||
'Something went wrong in the singleCleaner thread: a query did not return the requested fields. ' + repr(row))
|
||||
time.sleep(3)
|
||||
logger.error('Something went wrong in the singleCleaner thread: a query did not return the requested fields. ' + repr(row))
|
||||
self.stop.wait(3)
|
||||
break
|
||||
toAddress, ackData, status = row
|
||||
if status == 'awaitingpubkey':
|
||||
|
@ -123,7 +123,7 @@ class singleCleaner(threading.Thread):
|
|||
os._exit(0)
|
||||
shared.knownNodesLock.release()
|
||||
shared.needToWriteKnownNodesToDisk = False
|
||||
time.sleep(300)
|
||||
self.stop.wait(300)
|
||||
|
||||
|
||||
def resendPubkeyRequest(address):
|
||||
|
|
|
@ -4,6 +4,7 @@ import socket
|
|||
from class_sendDataThread import *
|
||||
from class_receiveDataThread import *
|
||||
import helper_bootstrap
|
||||
from helper_threading import *
|
||||
import errno
|
||||
import re
|
||||
|
||||
|
@ -15,10 +16,11 @@ import re
|
|||
# (within the recversion function of the recieveData thread)
|
||||
|
||||
|
||||
class singleListener(threading.Thread):
|
||||
class singleListener(threading.Thread, StoppableThread):
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
threading.Thread.__init__(self, name="singleListener")
|
||||
self.initStop()
|
||||
|
||||
def setup(self, selfInitiatedConnections):
|
||||
self.selfInitiatedConnections = selfInitiatedConnections
|
||||
|
@ -38,24 +40,33 @@ class singleListener(threading.Thread):
|
|||
sock.listen(2)
|
||||
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):
|
||||
# If there is a trusted peer then we don't want to accept
|
||||
# incoming connections so we'll just abandon the thread
|
||||
if shared.trustedPeer:
|
||||
return
|
||||
|
||||
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
|
||||
time.sleep(1)
|
||||
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect') and shared.shutdown == 0:
|
||||
self.stop.wait(1)
|
||||
helper_bootstrap.dns()
|
||||
# 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
|
||||
# proxy 'none' or configure SOCKS listening then this will start listening for
|
||||
# connections.
|
||||
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten'):
|
||||
time.sleep(5)
|
||||
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0:
|
||||
self.stop.wait(5)
|
||||
|
||||
with shared.printLock:
|
||||
print 'Listening for incoming connections.'
|
||||
logger.info('Listening for incoming connections.')
|
||||
|
||||
# First try listening on an IPv6 socket. This should also be
|
||||
# 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
|
||||
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
|
||||
# SOCKS proxy, unless they have configured otherwise. If they eventually select
|
||||
# proxy 'none' or configure SOCKS listening then this will start listening for
|
||||
# connections.
|
||||
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten'):
|
||||
time.sleep(10)
|
||||
while len(shared.connectedHostsList) > 220:
|
||||
with shared.printLock:
|
||||
print 'We are connected to too many people. Not accepting further incoming connections for ten seconds.'
|
||||
while shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not shared.config.getboolean('bitmessagesettings', 'sockslisten') and shared.shutdown == 0:
|
||||
self.stop.wait(10)
|
||||
while len(shared.connectedHostsList) > 220 and shared.shutdown == 0:
|
||||
logger.info('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()
|
||||
(HOST, PORT) = sockaddr[0:2]
|
||||
|
||||
|
@ -104,8 +114,7 @@ class singleListener(threading.Thread):
|
|||
# connection flooding.
|
||||
if HOST in shared.connectedHostsList:
|
||||
socketObject.close()
|
||||
with shared.printLock:
|
||||
print 'We are already connected to', HOST + '. Ignoring connection.'
|
||||
logger.info('We are already connected to ' + str(HOST) + '. Ignoring connection.')
|
||||
else:
|
||||
break
|
||||
|
||||
|
@ -124,6 +133,5 @@ class singleListener(threading.Thread):
|
|||
socketObject, HOST, PORT, -1, someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections, sendDataThreadQueue)
|
||||
rd.start()
|
||||
|
||||
with shared.printLock:
|
||||
print self, 'connected to', HOST, 'during INCOMING request.'
|
||||
logger.info('connected to ' + HOST + ' during INCOMING request.')
|
||||
|
||||
|
|
|
@ -15,17 +15,32 @@ from debug import logger
|
|||
from helper_sql import *
|
||||
import helper_inbox
|
||||
from helper_generic import addDataPadding
|
||||
from helper_threading import *
|
||||
import l10n
|
||||
|
||||
# This thread, of which there is only one, does the heavy lifting:
|
||||
# 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):
|
||||
# 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):
|
||||
|
||||
|
@ -49,10 +64,10 @@ class singleWorker(threading.Thread):
|
|||
'''SELECT ackdata FROM sent where (status='msgsent' OR status='doingmsgpow')''')
|
||||
for row in queryreturn:
|
||||
ackdata, = row
|
||||
print 'Watching for ackdata', ackdata.encode('hex')
|
||||
logger.info('Watching for ackdata ' + ackdata.encode('hex'))
|
||||
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.
|
||||
|
||||
queryreturn = sqlQuery(
|
||||
|
@ -68,7 +83,7 @@ class singleWorker(threading.Thread):
|
|||
# just in case there are any tasks for Broadcasts
|
||||
# that have yet to be sent.
|
||||
|
||||
while True:
|
||||
while shared.shutdown == 0:
|
||||
command, data = shared.workerQueue.get()
|
||||
if command == 'sendmessage':
|
||||
self.sendMsg()
|
||||
|
@ -80,10 +95,10 @@ class singleWorker(threading.Thread):
|
|||
self.sendOutOrStoreMyV3Pubkey(data)
|
||||
elif command == 'sendOutOrStoreMyV4Pubkey':
|
||||
self.sendOutOrStoreMyV4Pubkey(data)
|
||||
elif command == 'stopThread':
|
||||
return
|
||||
else:
|
||||
with shared.printLock:
|
||||
sys.stderr.write(
|
||||
'Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command)
|
||||
logger.error('Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command)
|
||||
|
||||
shared.workerQueue.task_done()
|
||||
|
||||
|
@ -114,9 +129,7 @@ class singleWorker(threading.Thread):
|
|||
privEncryptionKeyBase58 = shared.config.get(
|
||||
myAddress, 'privencryptionkey')
|
||||
except Exception as err:
|
||||
with shared.printLock:
|
||||
sys.stderr.write(
|
||||
'Error within doPOWForMyV2Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
|
||||
logger.error('Error within doPOWForMyV2Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
|
||||
return
|
||||
|
||||
privSigningKeyHex = shared.decodeWalletImportFormat(
|
||||
|
@ -133,10 +146,10 @@ class singleWorker(threading.Thread):
|
|||
|
||||
# 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))))
|
||||
print '(For pubkey message) Doing proof of work...'
|
||||
logger.info('(For pubkey message) Doing proof of work...')
|
||||
initialHash = hashlib.sha512(payload).digest()
|
||||
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
|
||||
|
||||
inventoryHash = calculateInventoryHash(payload)
|
||||
|
@ -145,8 +158,7 @@ class singleWorker(threading.Thread):
|
|||
objectType, streamNumber, payload, embeddedTime,'')
|
||||
shared.inventorySets[streamNumber].add(inventoryHash)
|
||||
|
||||
with shared.printLock:
|
||||
print 'broadcasting inv with hash:', inventoryHash.encode('hex')
|
||||
logger.info('broadcasting inv with hash: ' + inventoryHash.encode('hex'))
|
||||
|
||||
shared.broadcastToSendDataQueues((
|
||||
streamNumber, 'advertiseobject', inventoryHash))
|
||||
|
@ -171,8 +183,7 @@ class singleWorker(threading.Thread):
|
|||
#The address has been deleted.
|
||||
return
|
||||
if shared.safeConfigGetBoolean(myAddress, 'chan'):
|
||||
with shared.printLock:
|
||||
print 'This is a chan address. Not sending pubkey.'
|
||||
logger.info('This is a chan address. Not sending pubkey.')
|
||||
return
|
||||
status, addressVersionNumber, streamNumber, hash = decodeAddress(
|
||||
myAddress)
|
||||
|
@ -199,9 +210,7 @@ class singleWorker(threading.Thread):
|
|||
privEncryptionKeyBase58 = shared.config.get(
|
||||
myAddress, 'privencryptionkey')
|
||||
except Exception as err:
|
||||
with shared.printLock:
|
||||
sys.stderr.write(
|
||||
'Error within sendOutOrStoreMyV3Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
|
||||
logger.error('Error within sendOutOrStoreMyV3Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
|
||||
|
||||
return
|
||||
|
||||
|
@ -228,12 +237,10 @@ class singleWorker(threading.Thread):
|
|||
|
||||
# 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))))
|
||||
with shared.printLock:
|
||||
print '(For pubkey message) Doing proof of work...'
|
||||
logger.info('(For pubkey message) Doing proof of work...')
|
||||
initialHash = hashlib.sha512(payload).digest()
|
||||
trialValue, nonce = proofofwork.run(target, initialHash)
|
||||
with shared.printLock:
|
||||
print '(For pubkey message) Found proof of work. Nonce:', nonce
|
||||
logger.info('(For pubkey message) Found proof of work. Nonce: ' + str(nonce))
|
||||
|
||||
payload = pack('>Q', nonce) + payload
|
||||
inventoryHash = calculateInventoryHash(payload)
|
||||
|
@ -242,8 +249,7 @@ class singleWorker(threading.Thread):
|
|||
objectType, streamNumber, payload, embeddedTime,'')
|
||||
shared.inventorySets[streamNumber].add(inventoryHash)
|
||||
|
||||
with shared.printLock:
|
||||
print 'broadcasting inv with hash:', inventoryHash.encode('hex')
|
||||
logger.info('broadcasting inv with hash: ' + inventoryHash.encode('hex'))
|
||||
|
||||
shared.broadcastToSendDataQueues((
|
||||
streamNumber, 'advertiseobject', inventoryHash))
|
||||
|
@ -264,8 +270,7 @@ class singleWorker(threading.Thread):
|
|||
#The address has been deleted.
|
||||
return
|
||||
if shared.safeConfigGetBoolean(myAddress, 'chan'):
|
||||
with shared.printLock:
|
||||
print 'This is a chan address. Not sending pubkey.'
|
||||
logger.info('This is a chan address. Not sending pubkey.')
|
||||
return
|
||||
status, addressVersionNumber, streamNumber, hash = decodeAddress(
|
||||
myAddress)
|
||||
|
@ -285,9 +290,7 @@ class singleWorker(threading.Thread):
|
|||
privEncryptionKeyBase58 = shared.config.get(
|
||||
myAddress, 'privencryptionkey')
|
||||
except Exception as err:
|
||||
with shared.printLock:
|
||||
sys.stderr.write(
|
||||
'Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
|
||||
logger.error('Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err)
|
||||
return
|
||||
|
||||
privSigningKeyHex = shared.decodeWalletImportFormat(
|
||||
|
@ -326,10 +329,10 @@ class singleWorker(threading.Thread):
|
|||
|
||||
# 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))))
|
||||
print '(For pubkey message) Doing proof of work...'
|
||||
logger.info('(For pubkey message) Doing proof of work...')
|
||||
initialHash = hashlib.sha512(payload).digest()
|
||||
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
|
||||
inventoryHash = calculateInventoryHash(payload)
|
||||
|
@ -338,8 +341,7 @@ class singleWorker(threading.Thread):
|
|||
objectType, streamNumber, payload, embeddedTime, doubleHashOfAddressData[32:])
|
||||
shared.inventorySets[streamNumber].add(inventoryHash)
|
||||
|
||||
with shared.printLock:
|
||||
print 'broadcasting inv with hash:', inventoryHash.encode('hex')
|
||||
logger.info('broadcasting inv with hash: ' + inventoryHash.encode('hex'))
|
||||
|
||||
shared.broadcastToSendDataQueues((
|
||||
streamNumber, 'advertiseobject', inventoryHash))
|
||||
|
@ -359,9 +361,7 @@ class singleWorker(threading.Thread):
|
|||
status, addressVersionNumber, streamNumber, ripe = decodeAddress(
|
||||
fromaddress)
|
||||
if addressVersionNumber <= 1:
|
||||
with shared.printLock:
|
||||
sys.stderr.write(
|
||||
'Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n')
|
||||
logger.error('Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n')
|
||||
return
|
||||
# We need to convert our private keys to public keys in order
|
||||
# to include them.
|
||||
|
@ -442,12 +442,12 @@ class singleWorker(threading.Thread):
|
|||
dataToEncrypt, pubEncryptionKey.encode('hex'))
|
||||
|
||||
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', (
|
||||
ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast..."))))
|
||||
initialHash = hashlib.sha512(payload).digest()
|
||||
trialValue, nonce = proofofwork.run(target, initialHash)
|
||||
print '(For broadcast message) Found proof of work', trialValue, 'Nonce:', nonce
|
||||
logger.info('(For broadcast message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce))
|
||||
|
||||
payload = pack('>Q', nonce) + payload
|
||||
|
||||
|
@ -463,8 +463,7 @@ class singleWorker(threading.Thread):
|
|||
shared.inventory[inventoryHash] = (
|
||||
objectType, streamNumber, payload, embeddedTime, tag)
|
||||
shared.inventorySets[streamNumber].add(inventoryHash)
|
||||
with shared.printLock:
|
||||
print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex')
|
||||
logger.info('sending inv (within sendBroadcast function) for object: ' + inventoryHash.encode('hex'))
|
||||
shared.broadcastToSendDataQueues((
|
||||
streamNumber, 'advertiseobject', inventoryHash))
|
||||
|
||||
|
@ -612,8 +611,8 @@ class singleWorker(threading.Thread):
|
|||
shared.ackdataForWhichImWatching[ackdata] = 0
|
||||
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
|
||||
ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key"))))
|
||||
with shared.printLock:
|
||||
print 'Sending a message. First 150 characters of message:', repr(message[:150])
|
||||
logger.info('Sending a message.')
|
||||
logger.debug('First 150 characters of message: ' + repr(message[:150]))
|
||||
|
||||
# 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
|
||||
|
@ -685,8 +684,8 @@ class singleWorker(threading.Thread):
|
|||
requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(l10n.formatTimestamp()))))
|
||||
continue
|
||||
else: # if we are sending a message to ourselves or a chan..
|
||||
with shared.printLock:
|
||||
print 'Sending a message. First 150 characters of message:', repr(message[:150])
|
||||
logger.info('Sending a message.')
|
||||
logger.debug('First 150 characters of message: ' + repr(message[:150]))
|
||||
behaviorBitfield = '\x00\x00\x00\x01'
|
||||
|
||||
try:
|
||||
|
@ -694,9 +693,7 @@ class singleWorker(threading.Thread):
|
|||
toaddress, 'privencryptionkey')
|
||||
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()))))
|
||||
with shared.printLock:
|
||||
sys.stderr.write(
|
||||
'Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s\n' % err)
|
||||
logger.error('Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s\n' % err)
|
||||
continue
|
||||
privEncryptionKeyHex = shared.decodeWalletImportFormat(
|
||||
privEncryptionKeyBase58).encode('hex')
|
||||
|
@ -761,12 +758,10 @@ class singleWorker(threading.Thread):
|
|||
payload += encodeVarint(len(messageToTransmit))
|
||||
payload += messageToTransmit
|
||||
if shared.config.has_section(toaddress):
|
||||
with shared.printLock:
|
||||
print 'Not bothering to include ackdata because we are sending to ourselves or a chan.'
|
||||
logger.info('Not bothering to include ackdata because we are sending to ourselves or a chan.')
|
||||
fullAckPayload = ''
|
||||
elif not shared.isBitSetWithinBitfield(behaviorBitfield,31):
|
||||
with shared.printLock:
|
||||
print 'Not bothering to include ackdata because the receiver said that they won\'t relay it anyway.'
|
||||
logger.info('Not bothering to include ackdata because the receiver said that they won\'t relay it anyway.')
|
||||
fullAckPayload = ''
|
||||
else:
|
||||
fullAckPayload = self.generateFullAckMessage(
|
||||
|
@ -791,16 +786,14 @@ class singleWorker(threading.Thread):
|
|||
encryptedPayload += encodeVarint(1) # msg version
|
||||
encryptedPayload += encodeVarint(toStreamNumber) + encrypted
|
||||
target = 2 ** 64 / (requiredAverageProofOfWorkNonceTrialsPerByte*(len(encryptedPayload) + 8 + requiredPayloadLengthExtraBytes + ((TTL*(len(encryptedPayload)+8+requiredPayloadLengthExtraBytes))/(2 ** 16))))
|
||||
with shared.printLock:
|
||||
print '(For msg message) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes
|
||||
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)
|
||||
|
||||
powStartTime = time.time()
|
||||
initialHash = hashlib.sha512(encryptedPayload).digest()
|
||||
trialValue, nonce = proofofwork.run(target, initialHash)
|
||||
with shared.printLock:
|
||||
print '(For msg message) Found proof of work', trialValue, 'Nonce:', nonce
|
||||
logger.info('(For msg message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce))
|
||||
try:
|
||||
print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.'
|
||||
logger.info('PoW took %.1f seconds, speed %s.', time.time() - powStartTime, sizeof_fmt(nonce / (time.time() - powStartTime)))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -823,7 +816,7 @@ class singleWorker(threading.Thread):
|
|||
else:
|
||||
# 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()))))
|
||||
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((
|
||||
toStreamNumber, 'advertiseobject', inventoryHash))
|
||||
|
||||
|
@ -871,8 +864,7 @@ class singleWorker(threading.Thread):
|
|||
toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress(
|
||||
toAddress)
|
||||
if toStatus != 'success':
|
||||
with shared.printLock:
|
||||
sys.stderr.write('Very abnormal error occurred in requestPubKey. toAddress is: ' + repr(
|
||||
logger.error('Very abnormal error occurred in requestPubKey. toAddress is: ' + repr(
|
||||
toAddress) + '. Please report this error to Atheros.')
|
||||
return
|
||||
|
||||
|
@ -907,12 +899,10 @@ class singleWorker(threading.Thread):
|
|||
payload += encodeVarint(streamNumber)
|
||||
if addressVersionNumber <= 3:
|
||||
payload += ripe
|
||||
with shared.printLock:
|
||||
print 'making request for pubkey with ripe:', ripe.encode('hex')
|
||||
logger.info('making request for pubkey with ripe:', ripe.encode('hex'))
|
||||
else:
|
||||
payload += tag
|
||||
with shared.printLock:
|
||||
print 'making request for v4 pubkey with tag:', tag.encode('hex')
|
||||
logger.info('making request for v4 pubkey with tag:', tag.encode('hex'))
|
||||
|
||||
# print 'trial value', trialValue
|
||||
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))))
|
||||
initialHash = hashlib.sha512(payload).digest()
|
||||
trialValue, nonce = proofofwork.run(target, initialHash)
|
||||
with shared.printLock:
|
||||
print 'Found proof of work', trialValue, 'Nonce:', nonce
|
||||
logger.info('Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce))
|
||||
|
||||
payload = pack('>Q', nonce) + payload
|
||||
inventoryHash = calculateInventoryHash(payload)
|
||||
|
@ -932,7 +921,7 @@ class singleWorker(threading.Thread):
|
|||
shared.inventory[inventoryHash] = (
|
||||
objectType, streamNumber, payload, embeddedTime, '')
|
||||
shared.inventorySets[streamNumber].add(inventoryHash)
|
||||
print 'sending inv (for the getpubkey message)'
|
||||
logger.info('sending inv (for the getpubkey message)')
|
||||
shared.broadcastToSendDataQueues((
|
||||
streamNumber, 'advertiseobject', inventoryHash))
|
||||
|
||||
|
@ -948,7 +937,7 @@ class singleWorker(threading.Thread):
|
|||
toAddress)
|
||||
|
||||
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()))))
|
||||
|
||||
def generateFullAckMessage(self, ackdata, toStreamNumber, TTL):
|
||||
|
@ -976,16 +965,14 @@ class singleWorker(threading.Thread):
|
|||
payload += encodeVarint(toStreamNumber) + ackdata
|
||||
|
||||
target = 2 ** 64 / (shared.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + shared.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+shared.networkDefaultPayloadLengthExtraBytes))/(2 ** 16))))
|
||||
with shared.printLock:
|
||||
print '(For ack message) Doing proof of work. TTL set to', TTL
|
||||
logger.info('(For ack message) Doing proof of work. TTL set to ' + str(TTL))
|
||||
|
||||
powStartTime = time.time()
|
||||
initialHash = hashlib.sha512(payload).digest()
|
||||
trialValue, nonce = proofofwork.run(target, initialHash)
|
||||
with shared.printLock:
|
||||
print '(For ack message) Found proof of work', trialValue, 'Nonce:', nonce
|
||||
logger.info('(For ack message) Found proof of work ' + str(trialValue) + ' Nonce: ' + str(nonce))
|
||||
try:
|
||||
print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.'
|
||||
logger.info('PoW took %.1f seconds, speed %s.', time.time() - powStartTime, sizeof_fmt(nonce / (time.time() - powStartTime)))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
|
|
@ -19,13 +19,15 @@ import tr#anslate
|
|||
class sqlThread(threading.Thread):
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
threading.Thread.__init__(self, name="SQL")
|
||||
|
||||
def run(self):
|
||||
self.conn = sqlite3.connect(shared.appdata + 'messages.dat')
|
||||
self.conn.text_factory = str
|
||||
self.cur = self.conn.cursor()
|
||||
|
||||
self.cur.execute('PRAGMA secure_delete = true')
|
||||
|
||||
try:
|
||||
self.cur.execute(
|
||||
'''CREATE TABLE inbox (msgid blob, toaddress text, fromaddress text, subject text, received text, message text, folder text, encodingtype int, read bool, sighash blob, UNIQUE(msgid) ON CONFLICT REPLACE)''' )
|
||||
|
@ -450,7 +452,7 @@ class sqlThread(threading.Thread):
|
|||
queryreturn = self.cur.fetchall()
|
||||
for row in queryreturn:
|
||||
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...')
|
||||
try:
|
||||
self.cur.execute( ''' VACUUM ''')
|
||||
|
|
28
src/debug.py
28
src/debug.py
|
@ -18,15 +18,33 @@ Use: `from debug import logger` to import this facility into whatever module you
|
|||
'''
|
||||
import logging
|
||||
import logging.config
|
||||
import os
|
||||
import shared
|
||||
import sys
|
||||
import traceback
|
||||
import helper_startup
|
||||
helper_startup.loadConfig()
|
||||
|
||||
# TODO(xj9): Get from a config file.
|
||||
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():
|
||||
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({
|
||||
'version': 1,
|
||||
'formatters': {
|
||||
|
@ -69,13 +87,17 @@ def configureLogging():
|
|||
'handlers': ['console'],
|
||||
},
|
||||
})
|
||||
return True
|
||||
|
||||
# TODO (xj9): Get from a config file.
|
||||
#logger = logging.getLogger('console_only')
|
||||
configureLogging()
|
||||
if configureLogging():
|
||||
if '-c' in sys.argv:
|
||||
logger = logging.getLogger('file_only')
|
||||
else:
|
||||
logger = logging.getLogger('both')
|
||||
else:
|
||||
logger = logging.getLogger('default')
|
||||
|
||||
def restartLoggingInUpdatedAppdataLocation():
|
||||
global logger
|
||||
|
@ -83,9 +105,11 @@ def restartLoggingInUpdatedAppdataLocation():
|
|||
logger.removeHandler(i)
|
||||
i.flush()
|
||||
i.close()
|
||||
configureLogging()
|
||||
if configureLogging():
|
||||
if '-c' in sys.argv:
|
||||
logger = logging.getLogger('file_only')
|
||||
else:
|
||||
logger = logging.getLogger('both')
|
||||
else:
|
||||
logger = logging.getLogger('default')
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ import defaultKnownNodes
|
|||
import pickle
|
||||
import time
|
||||
|
||||
from debug import logger
|
||||
import socks
|
||||
|
||||
def knownNodes():
|
||||
try:
|
||||
# We shouldn't have to use the shared.knownNodesLock because this had
|
||||
|
@ -26,7 +29,7 @@ def knownNodes():
|
|||
except:
|
||||
shared.knownNodes = defaultKnownNodes.createDefaultKnownNodes(shared.appdata)
|
||||
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
|
||||
|
||||
def dns():
|
||||
|
@ -35,20 +38,53 @@ def dns():
|
|||
# defaultKnownNodes.py. Hopefully either they are up to date or the user
|
||||
# has run Bitmessage recently without SOCKS turned on and received good
|
||||
# bootstrap nodes using that method.
|
||||
with shared.printLock:
|
||||
if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none':
|
||||
try:
|
||||
for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80):
|
||||
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method'
|
||||
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:
|
||||
print 'bootstrap8080.bitmessage.org DNS bootstrapping failed.'
|
||||
logger.error('bootstrap8080.bitmessage.org DNS bootstrapping failed.')
|
||||
try:
|
||||
for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80):
|
||||
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method'
|
||||
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:
|
||||
print 'bootstrap8444.bitmessage.org DNS bootstrapping failed.'
|
||||
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:
|
||||
print 'DNS bootstrap skipped because SOCKS is used.'
|
||||
sock.setproxy(
|
||||
proxytype, sockshostname, socksport, rdns)
|
||||
try:
|
||||
ip = sock.resolve("bootstrap" + str(port) + ".bitmessage.org")
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
sock.close()
|
||||
except:
|
||||
logger.error("SOCKS DNS resolving failed", exc_info=True)
|
||||
if ip is not None:
|
||||
logger.info ('Adding ' + ip + ' to knownNodes based on SOCKS DNS bootstrap method')
|
||||
shared.knownNodes[1][shared.Peer(ip, port)] = time.time()
|
||||
else:
|
||||
logger.info('DNS bootstrap skipped because the proxy type does not support DNS resolution.')
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ import shared
|
|||
|
||||
def insert(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):
|
||||
sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', msgid)
|
||||
|
|
|
@ -11,6 +11,8 @@ def sqlQuery(sqlStatement, *args):
|
|||
|
||||
if args == ():
|
||||
sqlSubmitQueue.put('')
|
||||
elif type(args[0]) in [list, tuple]:
|
||||
sqlSubmitQueue.put(args[0])
|
||||
else:
|
||||
sqlSubmitQueue.put(args)
|
||||
|
||||
|
|
|
@ -145,5 +145,5 @@ def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
|
|||
return StrictVersion("5.1.2600")<=VER_THIS and StrictVersion("6.0.6000")>=VER_THIS
|
||||
return False
|
||||
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
|
||||
|
|
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
|
||||
from struct import unpack, pack
|
||||
import sys
|
||||
from shared import config, frozen
|
||||
import shared
|
||||
#import os
|
||||
from debug import logger
|
||||
from shared import config, frozen, codePath, shutdown, safeConfigGetBoolean, UISignalQueue
|
||||
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():
|
||||
if 'linux' in sys.platform:
|
||||
|
@ -32,14 +67,17 @@ def _pool_worker(nonce, initialHash, target, pool_size):
|
|||
return [trialValue, nonce]
|
||||
|
||||
def _doSafePoW(target, initialHash):
|
||||
logger.debug("Safe PoW start")
|
||||
nonce = 0
|
||||
trialValue = float('inf')
|
||||
while trialValue > target:
|
||||
nonce += 1
|
||||
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
|
||||
logger.debug("Safe PoW done")
|
||||
return [trialValue, nonce]
|
||||
|
||||
def _doFastPoW(target, initialHash):
|
||||
logger.debug("Fast PoW start")
|
||||
import time
|
||||
from multiprocessing import Pool, cpu_count
|
||||
try:
|
||||
|
@ -57,7 +95,7 @@ def _doFastPoW(target, initialHash):
|
|||
for i in range(pool_size):
|
||||
result.append(pool.apply_async(_pool_worker, args = (i, initialHash, target, pool_size)))
|
||||
while True:
|
||||
if shared.shutdown >= 1:
|
||||
if shutdown >= 1:
|
||||
pool.terminate()
|
||||
while True:
|
||||
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()
|
||||
pool.terminate()
|
||||
pool.join() #Wait for the workers to exit...
|
||||
logger.debug("Fast PoW done")
|
||||
return result[0], result[1]
|
||||
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):
|
||||
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:
|
||||
# on my (Peter Surda) Windows 10, Windows Defender
|
||||
# does not like this and fights with PyBitmessage
|
||||
# over CPU, resulting in very slow PoW
|
||||
# added on 2015-11-29: multiprocesing.freeze_support() doesn't help
|
||||
try:
|
||||
return _doFastPoW(target, initialHash)
|
||||
else:
|
||||
except:
|
||||
pass #fallback
|
||||
return _doSafePoW(target, initialHash)
|
|
@ -1,6 +1,6 @@
|
|||
from __future__ import division
|
||||
|
||||
softwareVersion = '0.4.4'
|
||||
softwareVersion = '0.5.5'
|
||||
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.
|
||||
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 helper_startup
|
||||
from helper_sql import *
|
||||
from helper_threading import *
|
||||
|
||||
|
||||
config = ConfigParser.SafeConfigParser()
|
||||
|
@ -83,7 +84,7 @@ lastTimeWeResetBytesSent = 0 # used for the bandwidth rate limit
|
|||
sendDataLock = threading.Lock() # used for the bandwidth rate limit
|
||||
receiveDataLock = threading.Lock() # used for the bandwidth rate limit
|
||||
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.
|
||||
maximumLengthOfTimeToBotherResendingMessages = 0
|
||||
objectProcessorQueue = Queue.Queue(
|
||||
|
@ -116,10 +117,17 @@ frozen = getattr(sys,'frozen', None)
|
|||
# security.
|
||||
trustedPeer = None
|
||||
|
||||
# For UPnP
|
||||
extPort = None
|
||||
|
||||
#Compiled struct for packing/unpacking headers
|
||||
#New code should use CreatePacket instead of Header.pack
|
||||
Header = Struct('!L12sL4s')
|
||||
|
||||
#Service flags
|
||||
NODE_NETWORK = 1
|
||||
NODE_SSL = 2
|
||||
|
||||
#Create a packet
|
||||
def CreatePacket(command, payload=''):
|
||||
payload_length = len(payload)
|
||||
|
@ -135,16 +143,26 @@ def isInSqlInventory(hash):
|
|||
return queryreturn != []
|
||||
|
||||
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' + \
|
||||
socket.inet_aton(host)
|
||||
else:
|
||||
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 += 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(
|
||||
|
@ -155,8 +173,10 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber):
|
|||
payload += pack('>q', 1) # bitflags of the services I offer.
|
||||
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.
|
||||
payload += pack('>H', shared.config.getint(
|
||||
'bitmessagesettings', 'port'))
|
||||
if safeConfigGetBoolean('bitmessagesettings', 'upnp') and extPort:
|
||||
payload += pack('>H', extPort)
|
||||
else:
|
||||
payload += pack('>H', shared.config.getint('bitmessagesettings', 'port'))
|
||||
|
||||
random.seed()
|
||||
payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf
|
||||
|
@ -218,6 +238,15 @@ def lookupAppdataFolder():
|
|||
dataFolder = 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):
|
||||
queryreturn = sqlQuery(
|
||||
'''select address from addressbook where address=?''',
|
||||
|
@ -385,6 +414,15 @@ def doCleanShutdown():
|
|||
# shutdown variable and exit. If the main thread closes before they do then they won't stop.
|
||||
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'):
|
||||
logger.info('Clean shutdown complete.')
|
||||
os._exit(0)
|
||||
|
|
|
@ -3,22 +3,26 @@
|
|||
import sys
|
||||
import os
|
||||
import errno
|
||||
import tempfile
|
||||
import shared
|
||||
from multiprocessing import Process
|
||||
|
||||
|
||||
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
|
||||
which is under the Python Software Foundation License version 2
|
||||
"""
|
||||
def __init__(self, flavor_id=""):
|
||||
def __init__(self, flavor_id="", daemon=False):
|
||||
import sys
|
||||
self.initialized = False
|
||||
basename = os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace("/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock'
|
||||
self.lockfile = os.path.normpath(tempfile.gettempdir() + '/' + basename)
|
||||
self.daemon = daemon;
|
||||
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':
|
||||
try:
|
||||
|
|
|
@ -155,7 +155,7 @@ class socksocket(socket.socket):
|
|||
"""
|
||||
self.__proxy = (proxytype, addr, port, rdns, username, password)
|
||||
|
||||
def __negotiatesocks5(self, destaddr, destport):
|
||||
def __negotiatesocks5(self):
|
||||
"""__negotiatesocks5(self,destaddr,destport)
|
||||
Negotiates a connection through a SOCKS5 server.
|
||||
"""
|
||||
|
@ -200,6 +200,8 @@ class socksocket(socket.socket):
|
|||
raise Socks5AuthError((2, _socks5autherrors[2]))
|
||||
else:
|
||||
raise GeneralProxyError((1, _generalerrors[1]))
|
||||
|
||||
def __connectsocks5(self, destaddr, destport):
|
||||
# Now we can request the actual connection
|
||||
req = struct.pack('BBB', 0x05, 0x01, 0x00)
|
||||
# If the given destination address is an IP address, we'll
|
||||
|
@ -247,6 +249,37 @@ class socksocket(socket.socket):
|
|||
else:
|
||||
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):
|
||||
"""getsockname() -> address info
|
||||
Returns the bound IP address and port number at the proxy.
|
||||
|
@ -361,7 +394,8 @@ class socksocket(socket.socket):
|
|||
else:
|
||||
portnum = 1080
|
||||
_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:
|
||||
if self.__proxy[2] != None:
|
||||
portnum = self.__proxy[2]
|
||||
|
@ -380,3 +414,15 @@ class socksocket(socket.socket):
|
|||
_orgsocket.connect(self, (destpair[0], destpair[1]))
|
||||
else:
|
||||
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>
|
||||
<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>
|
||||
<translation>تم نقل المادة لسلة المهملات، لا يتوفر واجهة مستخدم لإظهار سلة المهملات حالياً، و لكن يمكنك إيجاد الرسالة المحذوفة على القرص الصلب إذا أردت استرجاعها.</translation>
|
||||
<source>Moved items to trash.</source>
|
||||
<translation>تم نقل المادة لسلة المهملات.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<source>Moved items to trash.</source>
|
||||
<translation>Položky byly přesunuty do koše.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<source>Moved items to trash.</source>
|
||||
<translation>Objekt in den Papierkorb verschoben.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../bitmessageqt/__init__.py" line="2672"/>
|
||||
|
@ -905,7 +905,7 @@ p, li { white-space: pre-wrap; }
|
|||
</message>
|
||||
<message>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</message>
|
||||
<message>
|
||||
|
|
|
@ -491,8 +491,8 @@ Estas grava ke vi faru savkopion de tiu dosiero. Ĉu vi volas malfermi la dosier
|
|||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<translation>Movis elementojn al rubujo. Ne estas fasado por rigardi vian rubujon, sed ankoraŭ estas sur disko se vi esperas ĝin retrovi.</translation>
|
||||
<source>Moved items to trash.</source>
|
||||
<translation>Movis elementojn al rubujo.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../bitmessageqt/__init__.py" line="2672"/>
|
||||
|
@ -899,7 +899,7 @@ p, li { white-space: pre-wrap; }
|
|||
</message>
|
||||
<message>
|
||||
<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>
|
||||
</message>
|
||||
<message>
|
||||
|
|
|
@ -573,8 +573,8 @@ Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l'o
|
|||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<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>
|
||||
<source>Moved items to trash.</source>
|
||||
<translation>Messages déplacés dans la corbeille.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<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>
|
||||
<translation>アイテムをゴミ箱へ移動。ゴミ箱の内容を表示する画面はありませんが、もし元に戻したくなった場合に備えてディスク上に残されます。</translation>
|
||||
<source>Moved items to trash.</source>
|
||||
<translation>アイテムをゴミ箱へ移動。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<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>
|
||||
</message>
|
||||
<message>
|
||||
|
|
|
@ -573,8 +573,8 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne
|
|||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<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>
|
||||
<source>Moved items to trash.</source>
|
||||
<translation>Kastet innholdet.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<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>
|
||||
<translation>Удалено в корзину. Чтобы попасть в корзину, Вам нужно будет найти файл корзины на диске.</translation>
|
||||
<source>Moved items to trash.</source>
|
||||
<translation>Удалено в корзину.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<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>
|
||||
<translation>已经移动项目到回收站。没有图形化的界面可以查看您的回收站,不过如果您还想找回的话它还在您的硬盘上。</translation>
|
||||
<source>Moved items to trash.</source>
|
||||
<translation>已经移动项目到回收站。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../bitmessageqt/__init__.py" line="2730"/>
|
||||
|
@ -1065,7 +1065,7 @@ Receiver's required difficulty: %1 and %2</source>
|
|||
</message>
|
||||
<message>
|
||||
<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>
|
||||
</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