Runnable with both Python3 and Python2, with both PyQt5 and PyQt4 by using Qt.py #2250

Open
kashikoibumi wants to merge 125 commits from kashikoibumi/py3qt into v0.6
179 changed files with 5086 additions and 4481 deletions

View File

@ -72,6 +72,8 @@ function install_python(){
wine python -m pip install pytools==2020.2
echo "Upgrading pip"
wine python -m pip install --upgrade pip
# install pypiwin32 for win32com
wine python -m pip install pypiwin32
}
function install_pyqt(){
@ -82,6 +84,8 @@ function install_pyqt(){
echo "Installing PyQt-${PYQT_VERSION} 32b"
wine PyQt${PYQT_VERSION}-x32.exe /S /WX
fi
# and qtpy
wine python -m pip install qtpy
}
function install_openssl(){

View File

@ -216,8 +216,7 @@ autodoc_mock_imports = [
'pkg_resources',
'pycanberra',
'pyopencl',
'PyQt4',
'PyQt5',
'qtpy',
'qrcode',
'stem',
'xdg',

View File

@ -11,11 +11,13 @@ ingredients:
- python-msgpack
- python-qrcode
- python-qt4
- python-qtpy
- python-setuptools
- python-sip
- python-six
- python-xdg
- sni-qt
- xkb-data
exclude:
- libdb5.3
- libglib2.0-0

View File

@ -2,7 +2,7 @@
import collectd
import json
import xmlrpclib
from six.moves import xmlrpc_client as xmlrpclib
pybmurl = ""
api = ""

View File

@ -43,8 +43,16 @@ a = Analysis(
'setuptools.msvc', '_cffi_backend',
'plugins.menu_qrcode', 'plugins.proxyconfig_stem'
],
runtime_hooks=[os.path.join(hookspath, 'pyinstaller_rthook_plugins.py')],
excludes=excludes
# https://github.com/pyinstaller/pyinstaller/wiki/Recipe-PyQt4-API-Version
runtime_hooks = [
os.path.join(hookspath, hook) for hook in (
'pyinstaller_rthook_pyqt4.py',
'pyinstaller_rthook_plugins.py'
)],
excludes += [
'PyQt4.QtOpenGL','PyQt4.QtSql',
'PyQt4.QtSvg', 'PyQt4.QtTest', 'PyQt4.QtWebKit', 'PyQt4.QtXml',
'win32ui']
)
@ -90,9 +98,8 @@ a.datas += addTranslations()
a.datas += [('default.ini', os.path.join(srcPath, 'default.ini'), 'DATA')]
excluded_binaries = [
'QtOpenGL4.dll',
'QtSvg4.dll',
'QtXml4.dll',
'QtOpenGL4.dll', 'QtSql4.dll', 'QtSvg4.dll', 'QtTest4.dll',
'QtWebKit4.dll', 'QtXml4.dll'
]
a.binaries = TOC([x for x in a.binaries if x[0] not in excluded_binaries])

View File

@ -0,0 +1,10 @@
# https://github.com/pyinstaller/pyinstaller/wiki/Recipe-PyQt4-API-Version
import sip
sip.setapi(u'QDate', 2)
sip.setapi(u'QDateTime', 2)
sip.setapi(u'QString', 2)
sip.setapi(u'QTextStream', 2)
sip.setapi(u'QTime', 2)
sip.setapi(u'QUrl', 2)
sip.setapi(u'QVariant', 2)

3
py3start.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
python3 pybitmessage/bitmessagemain.py "$@"

3
revert_blob_to_text.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
python3 pybitmessage/revert_blob_to_text.py "$@"

View File

@ -4,6 +4,7 @@ import os
import platform
import shutil
import sys
import six
from importlib import import_module
from setuptools import setup, Extension
@ -83,7 +84,7 @@ if __name__ == "__main__":
'images/kivy/text_images*.png'
]}
if sys.version_info[0] == 3:
if six.PY3:
packages.extend(
[
'pybitmessage.bitmessagekivy',

View File

@ -187,7 +187,7 @@ def decodeAddress(address):
integer = decodeBase58(address)
if integer == 0:
status = 'invalidcharacters'
return status, 0, 0, ''
return status, 0, 0, b''
# after converting to hex, the string will be prepended
# with a 0x and appended with a L in python2
hexdata = hex(integer)[2:].rstrip('L')
@ -200,23 +200,23 @@ def decodeAddress(address):
if checksum != double_sha512(data[:-4])[0:4]:
status = 'checksumfailed'
return status, 0, 0, ''
return status, 0, 0, b''
try:
addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9])
except varintDecodeError as e:
logger.error(str(e))
status = 'varintmalformed'
return status, 0, 0, ''
return status, 0, 0, b''
if addressVersionNumber > 4:
logger.error('cannot decode address version numbers this high')
status = 'versiontoohigh'
return status, 0, 0, ''
return status, 0, 0, b''
elif addressVersionNumber == 0:
logger.error('cannot decode address version numbers of zero.')
status = 'versiontoohigh'
return status, 0, 0, ''
return status, 0, 0, b''
try:
streamNumber, bytesUsedByStreamNumber = \
@ -224,7 +224,7 @@ def decodeAddress(address):
except varintDecodeError as e:
logger.error(str(e))
status = 'varintmalformed'
return status, 0, 0, ''
return status, 0, 0, b''
status = 'success'
if addressVersionNumber == 1:
@ -242,21 +242,21 @@ def decodeAddress(address):
return status, addressVersionNumber, streamNumber, \
b'\x00\x00' + embeddedRipeData
elif len(embeddedRipeData) < 18:
return 'ripetooshort', 0, 0, ''
return 'ripetooshort', 0, 0, b''
elif len(embeddedRipeData) > 20:
return 'ripetoolong', 0, 0, ''
return 'otherproblem', 0, 0, ''
return 'ripetoolong', 0, 0, b''
return 'otherproblem', 0, 0, b''
elif addressVersionNumber == 4:
embeddedRipeData = \
data[bytesUsedByVersionNumber + bytesUsedByStreamNumber:-4]
if embeddedRipeData[0:1] == b'\x00':
# In order to enforce address non-malleability, encoded
# RIPE data must have NULL bytes removed from the front
return 'encodingproblem', 0, 0, ''
return 'encodingproblem', 0, 0, b''
elif len(embeddedRipeData) > 20:
return 'ripetoolong', 0, 0, ''
return 'ripetoolong', 0, 0, b''
elif len(embeddedRipeData) < 4:
return 'ripetooshort', 0, 0, ''
return 'ripetooshort', 0, 0, b''
x00string = b'\x00' * (20 - len(embeddedRipeData))
return status, addressVersionNumber, streamNumber, \
x00string + embeddedRipeData

View File

@ -67,9 +67,12 @@ import subprocess # nosec B404
import time
from binascii import hexlify, unhexlify
from struct import pack, unpack
import sqlite3
import six
from six.moves import configparser, http_client, xmlrpc_server
from six.moves.reprlib import repr
from dbcompat import dbstr
import helper_inbox
import helper_sent
@ -531,12 +534,12 @@ class BMRPCDispatcher(object):
message = shared.fixPotentiallyInvalidUTF8Data(message)
return {
'msgid': hexlify(msgid),
'toAddress': toAddress,
'fromAddress': fromAddress,
'toAddress': toAddress.decode("utf-8", "replace"),
'fromAddress': fromAddress.decode("utf-8", "replace"),
'subject': base64.b64encode(subject),
'message': base64.b64encode(message),
'encodingType': encodingtype,
'receivedTime': received,
'receivedTime': received.decode("utf-8", "replace"),
'read': read
}
@ -598,11 +601,12 @@ class BMRPCDispatcher(object):
"""
queryreturn = sqlQuery(
"SELECT label, address from addressbook WHERE label = ?",
label
dbstr(label)
) if label else sqlQuery("SELECT label, address from addressbook")
data = []
for label, address in queryreturn:
label = shared.fixPotentiallyInvalidUTF8Data(label)
address = address.decode("utf-8", "replace")
data.append({
'label': base64.b64encode(label),
'address': address
@ -618,12 +622,12 @@ class BMRPCDispatcher(object):
self._verifyAddress(address)
# TODO: add unique together constraint in the table
queryreturn = sqlQuery(
"SELECT address FROM addressbook WHERE address=?", address)
"SELECT address FROM addressbook WHERE address=?", dbstr(address))
if queryreturn != []:
raise APIError(
16, 'You already have this address in your address book.')
sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address)
sqlExecute("INSERT INTO addressbook VALUES(?,?)", dbstr(label), dbstr(address))
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
queues.UISignalQueue.put(('rerenderMessagelistToLabels', ''))
queues.UISignalQueue.put(('rerenderAddressBook', ''))
@ -635,7 +639,7 @@ class BMRPCDispatcher(object):
"""Delete an entry from address book."""
address = addBMIfNotPresent(address)
self._verifyAddress(address)
sqlExecute('DELETE FROM addressbook WHERE address=?', address)
sqlExecute('DELETE FROM addressbook WHERE address=?', dbstr(address))
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
queues.UISignalQueue.put(('rerenderMessagelistToLabels', ''))
queues.UISignalQueue.put(('rerenderAddressBook', ''))
@ -919,6 +923,7 @@ class BMRPCDispatcher(object):
" ORDER BY received"
)
return {"inboxMessages": [
self._dump_inbox_message(*data) for data in queryreturn
]}
@ -953,19 +958,31 @@ class BMRPCDispatcher(object):
23, 'Bool expected in readStatus, saw %s instead.'
% type(readStatus))
queryreturn = sqlQuery(
"SELECT read FROM inbox WHERE msgid=?", msgid)
"SELECT read FROM inbox WHERE msgid=?", sqlite3.Binary(msgid))
if len(queryreturn) < 1:
queryreturn = sqlQuery(
"SELECT read FROM inbox WHERE msgid=CAST(? AS TEXT)", msgid)
# UPDATE is slow, only update if status is different
try:
if (queryreturn[0][0] == 1) != readStatus:
sqlExecute(
rowcount = sqlExecute(
"UPDATE inbox set read = ? WHERE msgid=?",
readStatus, sqlite3.Binary(msgid))
if rowcount < 1:
rowcount = sqlExecute(
"UPDATE inbox set read = ? WHERE msgid=CAST(? AS TEXT)",
readStatus, msgid)
queues.UISignalQueue.put(('changedInboxUnread', None))
except IndexError:
pass
queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, received, message,"
" encodingtype, read FROM inbox WHERE msgid=?", msgid
" encodingtype, read FROM inbox WHERE msgid=?", sqlite3.Binary(msgid)
)
if len(queryreturn) < 1:
queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, received, message,"
" encodingtype, read FROM inbox WHERE msgid=CAST(? AS TEXT)", msgid
)
try:
return {"inboxMessage": [
@ -1018,7 +1035,7 @@ class BMRPCDispatcher(object):
queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, received,"
" message, encodingtype, read FROM inbox WHERE folder='inbox'"
" AND toAddress=?", toAddress)
" AND toAddress=?", dbstr(toAddress))
return {"inboxMessages": [
self._dump_inbox_message(*data) for data in queryreturn
]}
@ -1035,6 +1052,12 @@ class BMRPCDispatcher(object):
queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
" message, encodingtype, status, ackdata FROM sent WHERE msgid=?",
sqlite3.Binary(msgid)
)
if len(queryreturn) < 1:
queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
" message, encodingtype, status, ackdata FROM sent WHERE msgid=CAST(? AS TEXT)",
msgid
)
try:
@ -1055,7 +1078,7 @@ class BMRPCDispatcher(object):
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
" message, encodingtype, status, ackdata FROM sent"
" WHERE folder='sent' AND fromAddress=? ORDER BY lastactiontime",
fromAddress
dbstr(fromAddress)
)
return {"sentMessages": [
self._dump_sent_message(*data) for data in queryreturn
@ -1072,7 +1095,13 @@ class BMRPCDispatcher(object):
queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
" message, encodingtype, status, ackdata FROM sent"
" WHERE ackdata=?", ackData
" WHERE ackdata=?", sqlite3.Binary(ackData)
)
if len(queryreturn) < 1:
queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
" message, encodingtype, status, ackdata FROM sent"
" WHERE ackdata=CAST(? AS TEXT)", ackData
)
try:
@ -1093,7 +1122,9 @@ class BMRPCDispatcher(object):
# Trash if in inbox table
helper_inbox.trash(msgid)
# Trash if in sent table
sqlExecute("UPDATE sent SET folder='trash' WHERE msgid=?", msgid)
rowcount = sqlExecute("UPDATE sent SET folder='trash' WHERE msgid=?", sqlite3.Binary(msgid))
if rowcount < 1:
sqlExecute("UPDATE sent SET folder='trash' WHERE msgid=CAST(? AS TEXT)", msgid)
return 'Trashed message (assuming message existed).'
@command('trashInboxMessage')
@ -1107,7 +1138,9 @@ class BMRPCDispatcher(object):
def HandleTrashSentMessage(self, msgid):
"""Trash sent message by msgid (encoded in hex)."""
msgid = self._decode(msgid, "hex")
sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid)
rowcount = sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', sqlite3.Binary(msgid))
if rowcount < 1:
sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=CAST(? AS TEXT)''', msgid)
return 'Trashed sent message (assuming message existed).'
@command('sendMessage')
@ -1150,9 +1183,9 @@ class BMRPCDispatcher(object):
toLabel = ''
queryreturn = sqlQuery(
"SELECT label FROM addressbook WHERE address=?", toAddress)
"SELECT label FROM addressbook WHERE address=?", dbstr(toAddress))
try:
toLabel = queryreturn[0][0]
toLabel = queryreturn[0][0].decode("utf-8", "replace")
except IndexError:
pass
@ -1217,9 +1250,12 @@ class BMRPCDispatcher(object):
raise APIError(15, 'Invalid ackData object size.')
ackdata = self._decode(ackdata, "hex")
queryreturn = sqlQuery(
"SELECT status FROM sent where ackdata=?", ackdata)
"SELECT status FROM sent where ackdata=?", sqlite3.Binary(ackdata))
if len(queryreturn) < 1:
queryreturn = sqlQuery(
"SELECT status FROM sent where ackdata=CAST(? AS TEXT)", ackdata)
try:
return queryreturn[0][0]
return queryreturn[0][0].decode("utf-8", "replace")
except IndexError:
return 'notfound'
@ -1238,11 +1274,11 @@ class BMRPCDispatcher(object):
# First we must check to see if the address is already in the
# subscriptions list.
queryreturn = sqlQuery(
"SELECT * FROM subscriptions WHERE address=?", address)
"SELECT * FROM subscriptions WHERE address=?", dbstr(address))
if queryreturn:
raise APIError(16, 'You are already subscribed to that address.')
sqlExecute(
"INSERT INTO subscriptions VALUES (?,?,?)", label, address, True)
"INSERT INTO subscriptions VALUES (?,?,?)", dbstr(label), dbstr(address), True)
shared.reloadBroadcastSendersForWhichImWatching()
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
queues.UISignalQueue.put(('rerenderSubscriptions', ''))
@ -1256,7 +1292,7 @@ class BMRPCDispatcher(object):
"""
address = addBMIfNotPresent(address)
sqlExecute("DELETE FROM subscriptions WHERE address=?", address)
sqlExecute("DELETE FROM subscriptions WHERE address=?", dbstr(address))
shared.reloadBroadcastSendersForWhichImWatching()
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
queues.UISignalQueue.put(('rerenderSubscriptions', ''))
@ -1274,6 +1310,7 @@ class BMRPCDispatcher(object):
data = []
for label, address, enabled in queryreturn:
label = shared.fixPotentiallyInvalidUTF8Data(label)
address = address.decode("utf-8", "replace")
data.append({
'label': base64.b64encode(label),
'address': address,
@ -1354,7 +1391,9 @@ class BMRPCDispatcher(object):
"""Trash a sent message by ackdata (hex encoded)"""
# This API method should only be used when msgid is not available
ackdata = self._decode(ackdata, "hex")
sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=?", ackdata)
rowcount = sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=?", sqlite3.Binary(ackdata))
if rowcount < 1:
sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=CAST(? AS TEXT)", ackdata)
return 'Trashed sent message (assuming message existed).'
@command('disseminatePubkey')
@ -1421,19 +1460,29 @@ class BMRPCDispatcher(object):
# use it we'll need to fill out a field in our inventory database
# which is blank by default (first20bytesofencryptedmessage).
queryreturn = sqlQuery(
"SELECT hash, payload FROM inventory WHERE tag = ''"
" and objecttype = 2")
"SELECT hash, payload FROM inventory WHERE tag = ?"
" and objecttype = 2", sqlite3.Binary(b""))
if len(queryreturn) < 1:
queryreturn = sqlQuery(
"SELECT hash, payload FROM inventory WHERE tag = CAST(? AS TEXT)"
" and objecttype = 2", b"")
with SqlBulkExecute() as sql:
for hash01, payload in queryreturn:
readPosition = 16 # Nonce length + time length
# Stream Number length
readPosition += decodeVarint(
payload[readPosition:readPosition + 10])[1]
t = (payload[readPosition:readPosition + 32], hash01)
sql.execute("UPDATE inventory SET tag=? WHERE hash=?", *t)
t = (sqlite3.Binary(payload[readPosition:readPosition + 32]), sqlite3.Binary(hash01))
_, rowcount = sql.execute("UPDATE inventory SET tag=? WHERE hash=?", *t)
if rowcount < 1:
t = (sqlite3.Binary(payload[readPosition:readPosition + 32]), hash01)
sql.execute("UPDATE inventory SET tag=? WHERE hash=CAST(? AS TEXT)", *t)
queryreturn = sqlQuery(
"SELECT payload FROM inventory WHERE tag = ?", requestedHash)
"SELECT payload FROM inventory WHERE tag = ?", sqlite3.Binary(requestedHash))
if len(queryreturn) < 1:
queryreturn = sqlQuery(
"SELECT payload FROM inventory WHERE tag = CAST(? AS TEXT)", requestedHash)
return {"receivedMessageDatas": [
{'data': hexlify(payload)} for payload, in queryreturn
]}

View File

@ -21,7 +21,8 @@ import os
import socket
import sys
import time
import xmlrpclib
from six.moves import xmlrpc_client as xmlrpclib
from six.moves import input as raw_input
from bmconfigparser import config

View File

@ -10,13 +10,15 @@ Bitmessage commandline interface
# * python2-pythondialog
# * dialog
import ConfigParser
from six.moves import configparser
import curses
import os
import sys
import time
from textwrap import fill
from threading import Timer
import six
import sqlite3
from dialog import Dialog
import helper_sent
@ -30,6 +32,7 @@ import state
from addresses import addBMIfNotPresent, decodeAddress
from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery
from dbcompat import dbstr
# pylint: disable=global-statement
@ -105,7 +108,7 @@ def ascii(s):
"""ASCII values"""
r = ""
for c in s:
if ord(c) in range(128):
if six.byte2int(c) in range(128):
r += c
return r
@ -326,13 +329,13 @@ def handlech(c, stdscr):
if c != curses.ERR:
global inboxcur, addrcur, sentcur, subcur, abookcur, blackcur
if c in range(256):
if chr(c) in '12345678':
if six.int2byte(c) in '12345678':
global menutab
menutab = int(chr(c))
elif chr(c) == 'q':
menutab = int(six.int2byte(c))
elif six.int2byte(c) == 'q':
global quit_
quit_ = True
elif chr(c) == '\n':
elif six.int2byte(c) == '\n':
curses.curs_set(1)
d = Dialog(dialog="dialog")
if menutab == 1:
@ -358,7 +361,9 @@ def handlech(c, stdscr):
inbox[inboxcur][1] +
"\"")
data = "" # pyint: disable=redefined-outer-name
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0])
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", sqlite3.Binary(inbox[inboxcur][0]))
if len(ret) < 1:
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=CAST(? AS TEXT)", inbox[inboxcur][0])
if ret != []:
for row in ret:
data, = row
@ -367,12 +372,16 @@ def handlech(c, stdscr):
for i, item in enumerate(data.split("\n")):
msg += fill(item, replace_whitespace=False) + "\n"
scrollbox(d, unicode(ascii(msg)), 30, 80)
sqlExecute("UPDATE inbox SET read=1 WHERE msgid=?", inbox[inboxcur][0])
rowcount = sqlExecute("UPDATE inbox SET read=1 WHERE msgid=?", sqlite3.Binary(inbox[inboxcur][0]))
if rowcount < 1:
sqlExecute("UPDATE inbox SET read=1 WHERE msgid=CAST(? AS TEXT)", inbox[inboxcur][0])
inbox[inboxcur][7] = 1
else:
scrollbox(d, unicode("Could not fetch message."))
elif t == "2": # Mark unread
sqlExecute("UPDATE inbox SET read=0 WHERE msgid=?", inbox[inboxcur][0])
rowcount = sqlExecute("UPDATE inbox SET read=0 WHERE msgid=?", sqlite3.Binary(inbox[inboxcur][0]))
if rowcount < 1:
sqlExecute("UPDATE inbox SET read=0 WHERE msgid=CAST(? AS TEXT)", inbox[inboxcur][0])
inbox[inboxcur][7] = 0
elif t == "3": # Reply
curses.curs_set(1)
@ -396,11 +405,14 @@ def handlech(c, stdscr):
if not m[5][:4] == "Re: ":
subject = "Re: " + m[5]
body = ""
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", m[0])
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", sqlite3.Binary(m[0]))
if len(ret) < 1:
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=CAST(? AS TEXT)", m[0])
if ret != []:
body = "\n\n------------------------------------------------------\n"
for row in ret:
body, = row
body = body.decode("utf-8", "replace")
sendMessage(fromaddr, toaddr, ischan, subject, body, True)
dialogreset(stdscr)
@ -410,7 +422,7 @@ def handlech(c, stdscr):
r, t = d.inputbox("Label for address \"" + addr + "\"")
if r == d.DIALOG_OK:
label = t
sqlExecute("INSERT INTO addressbook VALUES (?,?)", label, addr)
sqlExecute("INSERT INTO addressbook VALUES (?,?)", dbstr(label), dbstr(addr))
# Prepend entry
addrbook.reverse()
addrbook.append([label, addr])
@ -422,17 +434,22 @@ def handlech(c, stdscr):
r, t = d.inputbox("Filename", init=inbox[inboxcur][5] + ".txt")
if r == d.DIALOG_OK:
msg = ""
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0])
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", sqlite3.Binary(inbox[inboxcur][0]))
if len(ret) < 1:
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=CAST(? AS TEXT)", inbox[inboxcur][0])
if ret != []:
for row in ret:
msg, = row
msg = msg.decode("utf-8", "replace")
fh = open(t, "a") # Open in append mode just in case
fh.write(msg)
fh.close()
else:
scrollbox(d, unicode("Could not fetch message."))
elif t == "6": # Move to trash
sqlExecute("UPDATE inbox SET folder='trash' WHERE msgid=?", inbox[inboxcur][0])
rowcount = sqlExecute("UPDATE inbox SET folder='trash' WHERE msgid=?", sqlite3.Binary(inbox[inboxcur][0]))
if rowcount < 1:
sqlExecute("UPDATE inbox SET folder='trash' WHERE msgid=CAST(? AS TEXT)", inbox[inboxcur][0])
del inbox[inboxcur]
scrollbox(d, unicode(
"Message moved to trash. There is no interface to view your trash,"
@ -463,7 +480,12 @@ def handlech(c, stdscr):
data = ""
ret = sqlQuery(
"SELECT message FROM sent WHERE subject=? AND ackdata=?",
sentbox[sentcur][4],
dbstr(sentbox[sentcur][4]),
sqlite3.Binary(sentbox[sentcur][6]))
if len(ret) < 1:
ret = sqlQuery(
"SELECT message FROM sent WHERE subject=? AND ackdata=CAST(? AS TEXT)",
dbstr(sentbox[sentcur][4]),
sentbox[sentcur][6])
if ret != []:
for row in ret:
@ -476,9 +498,14 @@ def handlech(c, stdscr):
else:
scrollbox(d, unicode("Could not fetch message."))
elif t == "2": # Move to trash
sqlExecute(
rowcount = sqlExecute(
"UPDATE sent SET folder='trash' WHERE subject=? AND ackdata=?",
sentbox[sentcur][4],
dbstr(sentbox[sentcur][4]),
sqlite3.Binary(sentbox[sentcur][6]))
if rowcount < 1:
rowcount = sqlExecute(
"UPDATE sent SET folder='trash' WHERE subject=? AND ackdata=CAST(? AS TEXT)",
dbstr(sentbox[sentcur][4]),
sentbox[sentcur][6])
del sentbox[sentcur]
scrollbox(d, unicode(
@ -672,7 +699,7 @@ def handlech(c, stdscr):
elif t == "2" and m is False:
try:
mn = config.get(a, "mailinglistname")
except ConfigParser.NoOptionError:
except configparser.NoOptionError:
mn = ""
r, t = d.inputbox("Mailing list name", init=mn)
if r == d.DIALOG_OK:
@ -711,29 +738,29 @@ def handlech(c, stdscr):
subscriptions.append([label, addr, True])
subscriptions.reverse()
sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", label, addr, True)
sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", dbstr(label), dbstr(addr), True)
shared.reloadBroadcastSendersForWhichImWatching()
elif t == "2":
r, t = d.inputbox("Type in \"I want to delete this subscription\"")
if r == d.DIALOG_OK and t == "I want to delete this subscription":
sqlExecute(
"DELETE FROM subscriptions WHERE label=? AND address=?",
subscriptions[subcur][0],
subscriptions[subcur][1])
dbstr(subscriptions[subcur][0]),
dbstr(subscriptions[subcur][1]))
shared.reloadBroadcastSendersForWhichImWatching()
del subscriptions[subcur]
elif t == "3":
sqlExecute(
"UPDATE subscriptions SET enabled=1 WHERE label=? AND address=?",
subscriptions[subcur][0],
subscriptions[subcur][1])
dbstr(subscriptions[subcur][0]),
dbstr(subscriptions[subcur][1]))
shared.reloadBroadcastSendersForWhichImWatching()
subscriptions[subcur][2] = True
elif t == "4":
sqlExecute(
"UPDATE subscriptions SET enabled=0 WHERE label=? AND address=?",
subscriptions[subcur][0],
subscriptions[subcur][1])
dbstr(subscriptions[subcur][0]),
dbstr(subscriptions[subcur][1]))
shared.reloadBroadcastSendersForWhichImWatching()
subscriptions[subcur][2] = False
elif menutab == 6:
@ -762,7 +789,7 @@ def handlech(c, stdscr):
subscriptions.append([label, addr, True])
subscriptions.reverse()
sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", label, addr, True)
sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", dbstr(label), dbstr(addr), True)
shared.reloadBroadcastSendersForWhichImWatching()
elif t == "3":
r, t = d.inputbox("Input new address")
@ -771,7 +798,7 @@ def handlech(c, stdscr):
if addr not in [item[1] for i, item in enumerate(addrbook)]:
r, t = d.inputbox("Label for address \"" + addr + "\"")
if r == d.DIALOG_OK:
sqlExecute("INSERT INTO addressbook VALUES (?,?)", t, addr)
sqlExecute("INSERT INTO addressbook VALUES (?,?)", dbstr(t), dbstr(addr))
# Prepend entry
addrbook.reverse()
addrbook.append([t, addr])
@ -783,8 +810,8 @@ def handlech(c, stdscr):
if r == d.DIALOG_OK and t == "I want to delete this Address Book entry":
sqlExecute(
"DELETE FROM addressbook WHERE label=? AND address=?",
addrbook[abookcur][0],
addrbook[abookcur][1])
dbstr(addrbook[abookcur][0]),
dbstr(addrbook[abookcur][1]))
del addrbook[abookcur]
elif menutab == 7:
set_background_title(d, "Blacklist Dialog Box")
@ -800,20 +827,20 @@ def handlech(c, stdscr):
if r == d.DIALOG_OK and t == "I want to delete this Blacklist entry":
sqlExecute(
"DELETE FROM blacklist WHERE label=? AND address=?",
blacklist[blackcur][0],
blacklist[blackcur][1])
dbstr(blacklist[blackcur][0]),
dbstr(blacklist[blackcur][1]))
del blacklist[blackcur]
elif t == "2":
sqlExecute(
"UPDATE blacklist SET enabled=1 WHERE label=? AND address=?",
blacklist[blackcur][0],
blacklist[blackcur][1])
dbstr(blacklist[blackcur][0]),
dbstr(blacklist[blackcur][1]))
blacklist[blackcur][2] = True
elif t == "3":
sqlExecute(
"UPDATE blacklist SET enabled=0 WHERE label=? AND address=?",
blacklist[blackcur][0],
blacklist[blackcur][1])
dbstr(blacklist[blackcur][0]),
dbstr(blacklist[blackcur][1]))
blacklist[blackcur][2] = False
dialogreset(stdscr)
else:
@ -991,10 +1018,13 @@ def loadInbox():
ret = sqlQuery("""SELECT msgid, toaddress, fromaddress, subject, received, read
FROM inbox WHERE folder='inbox' AND %s LIKE ?
ORDER BY received
""" % (where,), what)
""" % (where,), dbstr(what))
for row in ret:
msgid, toaddr, fromaddr, subject, received, read = row
toaddr = toaddr.decode("utf-8", "replace")
fromaddr = fromaddr.decode("utf-8", "replace")
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
received = received.decode("utf-8", "replace")
# Set label for to address
try:
@ -1013,18 +1043,19 @@ def loadInbox():
if config.has_section(fromaddr):
fromlabel = config.get(fromaddr, "label")
if fromlabel == "": # Check Address Book
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", fromaddr)
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", dbstr(fromaddr))
if qr != []:
for r in qr:
fromlabel, = r
fromlabel = shared.fixPotentiallyInvalidUTF8Data(fromlabel)
if fromlabel == "": # Check Subscriptions
qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", fromaddr)
qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", dbstr(fromaddr))
if qr != []:
for r in qr:
fromlabel, = r
fromlabel = shared.fixPotentiallyInvalidUTF8Data(fromlabel)
if fromlabel == "":
fromlabel = fromaddr
fromlabel = shared.fixPotentiallyInvalidUTF8Data(fromlabel)
# Load into array
inbox.append([
@ -1044,22 +1075,27 @@ def loadSent():
ret = sqlQuery("""SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
FROM sent WHERE folder='sent' AND %s LIKE ?
ORDER BY lastactiontime
""" % (where,), what)
""" % (where,), dbstr(what))
for row in ret:
toaddr, fromaddr, subject, status, ackdata, lastactiontime = row
toaddr = toaddr.decode("utf-8", "replace")
fromaddr = fromaddr.decode("utf-8", "replace")
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
status = status.decode("utf-8", "replace")
# Set label for to address
tolabel = ""
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", toaddr)
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", dbstr(toaddr))
if qr != []:
for r in qr:
tolabel, = r
tolabel = tolabel.decode("utf-8", "replace")
if tolabel == "":
qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", toaddr)
qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", dbstr(toaddr))
if qr != []:
for r in qr:
tolabel, = r
tolabel = tolabel.decode("utf-8", "replace")
if tolabel == "":
if config.has_section(toaddr):
tolabel = config.get(toaddr, "label")
@ -1129,6 +1165,7 @@ def loadAddrBook():
for row in ret:
label, addr = row
label = shared.fixPotentiallyInvalidUTF8Data(label)
addr = addr.decode("utf-8", "replace")
addrbook.append([label, addr])
addrbook.reverse()
@ -1138,6 +1175,8 @@ def loadSubscriptions():
ret = sqlQuery("SELECT label, address, enabled FROM subscriptions")
for row in ret:
label, address, enabled = row
label = label.decode("utf-8", "replace")
address = address.decode("utf-8", "replace")
subscriptions.append([label, address, enabled])
subscriptions.reverse()
@ -1152,6 +1191,8 @@ def loadBlackWhiteList():
ret = sqlQuery("SELECT label, address, enabled FROM whitelist")
for row in ret:
label, address, enabled = row
label = label.decode("utf-8", "replace")
address = address.decode("utf-8", "replace")
blacklist.append([label, address, enabled])
blacklist.reverse()

View File

@ -29,6 +29,7 @@ from pybitmessage.bitmessagekivy.baseclass.common import (
from pybitmessage.bitmessagekivy.baseclass.popup import SavedAddressDetailPopup
from pybitmessage.bitmessagekivy.baseclass.addressbook_widgets import HelperAddressBook
from pybitmessage.helper_sql import sqlExecute
from dbcompat import dbstr
logger = logging.getLogger('default')
@ -59,7 +60,7 @@ class AddressBook(Screen, HelperAddressBook):
self.ids.tag_label.text = ''
self.queryreturn = kivy_helper_search.search_sql(
xAddress, account, "addressbook", where, what, False)
self.queryreturn = [obj for obj in reversed(self.queryreturn)]
self.queryreturn = [[obj[0].decode("utf-8", "replace"), obj[1].decode("utf-8", "replace")] for obj in reversed(self.queryreturn)]
if self.queryreturn:
self.ids.tag_label.text = 'Address Book'
self.has_refreshed = True
@ -131,7 +132,7 @@ class AddressBook(Screen, HelperAddressBook):
if self.ids.ml.children is not None:
self.ids.tag_label.text = ''
sqlExecute(
"DELETE FROM addressbook WHERE address = ?", address)
"DELETE FROM addressbook WHERE address = ?", dbstr(address))
toast('Address Deleted')
def close_pop(self, instance):
@ -142,8 +143,13 @@ class AddressBook(Screen, HelperAddressBook):
def update_addbook_label(self, instance):
"""Updating the label of address book address"""
address_list = kivy_helper_search.search_sql(folder="addressbook")
stored_labels = [labels[0] for labels in address_list]
add_dict = dict(address_list)
stored_labels = [labels[0].decode("utf-8", "replace") for labels in address_list]
add_dict = {}
for row in address_list:
label, address = row
label = label.decode("utf-8", "replace")
address = address.decode("utf-8", "replace")
add_dict[label] = address
label = str(self.addbook_popup.content_cls.ids.add_label.text)
if label in stored_labels and self.address == add_dict[label]:
stored_labels.remove(label)
@ -151,7 +157,7 @@ class AddressBook(Screen, HelperAddressBook):
sqlExecute("""
UPDATE addressbook
SET label = ?
WHERE address = ?""", label, self.addbook_popup.content_cls.address)
WHERE address = ?""", dbstr(label), dbstr(self.addbook_popup.content_cls.address))
App.get_running_app().root.ids.id_addressbook.ids.ml.clear_widgets()
App.get_running_app().root.ids.id_addressbook.loadAddresslist(None, 'All', '')
self.addbook_popup.dismiss()

View File

@ -7,6 +7,7 @@ Maildetail screen for inbox, sent, draft and trash.
import os
from datetime import datetime
import sqlite3
from kivy.core.clipboard import Clipboard
from kivy.clock import Clock
@ -111,7 +112,11 @@ class MailDetail(Screen): # pylint: disable=too-many-instance-attributes
elif self.kivy_state.detail_page_type == 'inbox':
data = sqlQuery(
"select toaddress, fromaddress, subject, message, received from inbox"
" where msgid = ?", self.kivy_state.mail_id)
" where msgid = ?", sqlite3.Binary(self.kivy_state.mail_id))
if len(data) < 1:
data = sqlQuery(
"select toaddress, fromaddress, subject, message, received from inbox"
" where msgid = CAST(? AS TEXT)", self.kivy_state.mail_id)
self.assign_mail_details(data)
App.get_running_app().set_mail_detail_header()
except Exception as e: # pylint: disable=unused-variable
@ -119,16 +124,16 @@ class MailDetail(Screen): # pylint: disable=too-many-instance-attributes
def assign_mail_details(self, data):
"""Assigning mail details"""
subject = data[0][2].decode() if isinstance(data[0][2], bytes) else data[0][2]
body = data[0][3].decode() if isinstance(data[0][2], bytes) else data[0][3]
self.to_addr = data[0][0] if len(data[0][0]) > 4 else ' '
self.from_addr = data[0][1]
subject = data[0][2].decode("utf-8", "replace") if isinstance(data[0][2], bytes) else data[0][2]
body = data[0][3].decode("utf-8", "replace") if isinstance(data[0][2], bytes) else data[0][3]
self.to_addr = data[0][0].decode("utf-8", "replace") if len(data[0][0]) > 4 else ' '
self.from_addr = data[0][1].decode("utf-8", "replace")
self.subject = subject.capitalize(
) if subject.capitalize() else self.no_subject
self.message = body
if len(data[0]) == 7:
self.status = data[0][4]
self.status = data[0][4].decode("utf-8", "replace")
self.time_tag = show_time_history(data[0][4]) if self.kivy_state.detail_page_type == 'inbox' \
else show_time_history(data[0][6])
self.avatarImg = os.path.join(self.kivy_state.imageDir, 'draft-icon.png') \

View File

@ -59,7 +59,7 @@ class AddAddressPopup(BoxLayout):
"""Checking address is valid or not"""
my_addresses = (
App.get_running_app().root.ids.content_drawer.ids.identity_dropdown.values)
add_book = [addr[1] for addr in kivy_helper_search.search_sql(
add_book = [addr[1].decode("utf-8", "replace") for addr in kivy_helper_search.search_sql(
folder="addressbook")]
entered_text = str(instance.text).strip()
if entered_text in add_book:
@ -84,7 +84,7 @@ class AddAddressPopup(BoxLayout):
def checkLabel_valid(self, instance):
"""Checking address label is unique or not"""
entered_label = instance.text.strip()
addr_labels = [labels[0] for labels in kivy_helper_search.search_sql(
addr_labels = [labels[0].decode("utf-8", "replace") for labels in kivy_helper_search.search_sql(
folder="addressbook")]
if entered_label in addr_labels:
self.ids.label.error = True
@ -125,8 +125,13 @@ class SavedAddressDetailPopup(BoxLayout):
"""Checking address label is unique of not"""
entered_label = str(instance.text.strip())
address_list = kivy_helper_search.search_sql(folder="addressbook")
addr_labels = [labels[0] for labels in address_list]
add_dict = dict(address_list)
addr_labels = [labels[0].decode("utf-8", "replace") for labels in address_list]
add_dict = {}
for row in address_list:
label, address = row
label = label.decode("utf-8", "replace")
address = address.decode("utf-8", "replace")
add_dict[label] = address
if self.address and entered_label in addr_labels \
and self.address != add_dict[entered_label]:
self.ids.add_label.error = True

View File

@ -3,7 +3,7 @@ Core classes for loading images and converting them to a Texture.
The raw image data can be keep in memory for further access
"""
import hashlib
from io import BytesIO
from six import BytesIO
from PIL import Image
from kivy.core.image import Image as CoreImage

View File

@ -2,6 +2,7 @@
Sql queries for bitmessagekivy
"""
from pybitmessage.helper_sql import sqlQuery
from dbcompat import dbstr
def search_sql(
@ -30,21 +31,21 @@ def search_sql(
if account is not None:
if xAddress == 'both':
sqlStatementParts.append("(fromaddress = ? OR toaddress = ?)")
sqlArguments.append(account)
sqlArguments.append(account)
sqlArguments.append(dbstr(account))
sqlArguments.append(dbstr(account))
else:
sqlStatementParts.append(xAddress + " = ? ")
sqlArguments.append(account)
sqlArguments.append(dbstr(account))
if folder != "addressbook":
if folder is not None:
if folder == "new":
folder = "inbox"
unreadOnly = True
sqlStatementParts.append("folder = ? ")
sqlArguments.append(folder)
sqlArguments.append(dbstr(folder))
else:
sqlStatementParts.append("folder != ?")
sqlArguments.append("trash")
sqlArguments.append(dbstr("trash"))
if what is not None:
for colmns in where:
if len(where) > 1:
@ -54,7 +55,7 @@ def search_sql(
filter_col += " or %s LIKE ? )" % (colmns)
else:
filter_col = "%s LIKE ?" % (colmns)
sqlArguments.append(what)
sqlArguments.append(dbstr(what))
sqlStatementParts.append(filter_col)
if unreadOnly:
sqlStatementParts.append("read = 0")

View File

@ -6,6 +6,7 @@ import os
import shutil
import tempfile
from time import time, sleep
from six.moves import getcwdb
from requests.exceptions import ChunkedEncodingError
@ -34,7 +35,7 @@ def cleanup(files=_files):
class TeleniumTestProcess(TeleniumTestCase):
"""Setting Screen Functionality Testing"""
cmd_entrypoint = [os.path.join(os.path.abspath(os.getcwd()), 'src', 'mockbm', 'kivy_main.py')]
cmd_entrypoint = [os.path.join(os.path.abspath(getcwdb()), 'src', 'mockbm', 'kivy_main.py')]
@classmethod
def setUpClass(cls):

View File

@ -4,6 +4,7 @@ PyQt based UI for bitmessage, the main module
import hashlib
import locale
import logging
import os
import random
import string
@ -14,46 +15,52 @@ import threading
import time
from datetime import datetime, timedelta
from sqlite3 import register_adapter
import sqlite3
import six
from six.moves import range as xrange
if six.PY3:
from codecs import escape_decode
from PyQt4 import QtCore, QtGui
from PyQt4.QtNetwork import QLocalSocket, QLocalServer
from unqstr import ustr, unic
from dbcompat import dbstr
from qtpy import QtCore, QtGui, QtWidgets, QtNetwork
import shared
import state
from debug import logger
from tr import _translate
from account import (
from .account import (
accountClass, getSortedSubscriptions,
BMAccount, GatewayAccount, MailchuckAccount, AccountColor)
from addresses import decodeAddress, addBMIfNotPresent
from bitmessageui import Ui_MainWindow
from bitmessageqt.bitmessageui import Ui_MainWindow
from bmconfigparser import config
import namecoin
from messageview import MessageView
from migrationwizard import Ui_MigrationWizard
from foldertree import (
from .messageview import MessageView
from .migrationwizard import Ui_MigrationWizard
from .foldertree import (
AccountMixin, Ui_FolderWidget, Ui_AddressWidget, Ui_SubscriptionWidget,
MessageList_AddressWidget, MessageList_SubjectWidget,
Ui_AddressBookWidgetItemLabel, Ui_AddressBookWidgetItemAddress,
MessageList_TimeWidget)
import settingsmixin
import support
from bitmessageqt import settingsmixin
from bitmessageqt import support
from helper_sql import sqlQuery, sqlExecute, sqlExecuteChunked, sqlStoredProcedure
import helper_addressbook
import helper_search
import l10n
from utils import str_broadcast_subscribers, avatarize
import dialogs
from .utils import str_broadcast_subscribers, avatarize
from bitmessageqt import dialogs
from network.stats import pendingDownload, pendingUpload
from uisignaler import UISignaler
from .uisignaler import UISignaler
import paths
from proofofwork import getPowType
import queues
import shutdown
from statusbar import BMStatusBar
import sound
from .statusbar import BMStatusBar
from bitmessageqt import sound
# This is needed for tray icon
import bitmessage_icons_rc # noqa:F401 pylint: disable=unused-import
from bitmessageqt import bitmessage_icons_rc # noqa:F401 pylint: disable=unused-import
import helper_sent
try:
@ -61,6 +68,7 @@ try:
except ImportError:
get_plugins = False
logger = logging.getLogger('default')
is_windows = sys.platform.startswith('win')
@ -87,6 +95,13 @@ def openKeysFile():
os.startfile(keysfile) # pylint: disable=no-member
def as_msgid(id_data):
if six.PY3:
return escape_decode(id_data)[0][2:-1]
else: # assume six.PY2
return id_data
class MyForm(settingsmixin.SMainWindow):
# the maximum frequency of message sounds in seconds
@ -102,12 +117,12 @@ class MyForm(settingsmixin.SMainWindow):
newlocale = l10n.getTranslationLanguage()
try:
if not self.qmytranslator.isEmpty():
QtGui.QApplication.removeTranslator(self.qmytranslator)
QtWidgets.QApplication.removeTranslator(self.qmytranslator)
except:
pass
try:
if not self.qsystranslator.isEmpty():
QtGui.QApplication.removeTranslator(self.qsystranslator)
QtWidgets.QApplication.removeTranslator(self.qsystranslator)
except:
pass
@ -115,7 +130,7 @@ class MyForm(settingsmixin.SMainWindow):
translationpath = os.path.join(
paths.codePath(), 'translations', 'bitmessage_' + newlocale)
self.qmytranslator.load(translationpath)
QtGui.QApplication.installTranslator(self.qmytranslator)
QtWidgets.QApplication.installTranslator(self.qmytranslator)
self.qsystranslator = QtCore.QTranslator()
if paths.frozen:
@ -123,10 +138,10 @@ class MyForm(settingsmixin.SMainWindow):
paths.codePath(), 'translations', 'qt_' + newlocale)
else:
translationpath = os.path.join(
str(QtCore.QLibraryInfo.location(
ustr(QtCore.QLibraryInfo.location(
QtCore.QLibraryInfo.TranslationsPath)), 'qt_' + newlocale)
self.qsystranslator.load(translationpath)
QtGui.QApplication.installTranslator(self.qsystranslator)
QtWidgets.QApplication.installTranslator(self.qsystranslator)
# TODO: move this block into l10n
# FIXME: shouldn't newlocale be used here?
@ -151,50 +166,35 @@ class MyForm(settingsmixin.SMainWindow):
logger.error("Failed to set locale to %s", lang, exc_info=True)
def init_file_menu(self):
QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL(
"triggered()"), self.quit)
QtCore.QObject.connect(self.ui.actionNetworkSwitch, QtCore.SIGNAL(
"triggered()"), self.network_switch)
QtCore.QObject.connect(self.ui.actionManageKeys, QtCore.SIGNAL(
"triggered()"), self.click_actionManageKeys)
QtCore.QObject.connect(self.ui.actionDeleteAllTrashedMessages,
QtCore.SIGNAL(
"triggered()"),
self.ui.actionExit.triggered.connect(self.quit)
self.ui.actionNetworkSwitch.triggered.connect(self.network_switch)
self.ui.actionManageKeys.triggered.connect(self.click_actionManageKeys)
self.ui.actionDeleteAllTrashedMessages.triggered.connect(
self.click_actionDeleteAllTrashedMessages)
QtCore.QObject.connect(self.ui.actionRegenerateDeterministicAddresses,
QtCore.SIGNAL(
"triggered()"),
self.ui.actionRegenerateDeterministicAddresses.triggered.connect(
self.click_actionRegenerateDeterministicAddresses)
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.pushButtonAddAddressBook, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonAddAddressBook)
QtCore.QObject.connect(self.ui.pushButtonAddSubscription, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonAddSubscription)
QtCore.QObject.connect(self.ui.pushButtonTTL, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonTTL)
QtCore.QObject.connect(self.ui.pushButtonClear, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonClear)
QtCore.QObject.connect(self.ui.pushButtonSend, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonSend)
QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonFetchNamecoinID)
QtCore.QObject.connect(self.ui.actionSettings, QtCore.SIGNAL(
"triggered()"), self.click_actionSettings)
QtCore.QObject.connect(self.ui.actionAbout, QtCore.SIGNAL(
"triggered()"), self.click_actionAbout)
QtCore.QObject.connect(self.ui.actionSupport, QtCore.SIGNAL(
"triggered()"), self.click_actionSupport)
QtCore.QObject.connect(self.ui.actionHelp, QtCore.SIGNAL(
"triggered()"), self.click_actionHelp)
self.ui.actionSettings.triggered.connect(self.click_actionSettings)
self.ui.actionAbout.triggered.connect(self.click_actionAbout)
self.ui.actionSupport.triggered.connect(self.click_actionSupport)
self.ui.actionHelp.triggered.connect(self.click_actionHelp)
# also used for creating chans.
self.ui.pushButtonAddChan.clicked.connect(self.click_actionJoinChan)
self.ui.pushButtonNewAddress.clicked.connect(
self.click_NewAddressDialog)
self.ui.pushButtonAddAddressBook.clicked.connect(
self.click_pushButtonAddAddressBook)
self.ui.pushButtonAddSubscription.clicked.connect(
self.click_pushButtonAddSubscription)
self.ui.pushButtonTTL.clicked.connect(self.click_pushButtonTTL)
self.ui.pushButtonClear.clicked.connect(self.click_pushButtonClear)
self.ui.pushButtonSend.clicked.connect(self.click_pushButtonSend)
self.ui.pushButtonFetchNamecoinID.clicked.connect(
self.click_pushButtonFetchNamecoinID)
def init_inbox_popup_menu(self, connectSignal=True):
# Popup menu for the Inbox tab
self.ui.inboxContextMenuToolbar = QtGui.QToolBar()
self.ui.inboxContextMenuToolbar = QtWidgets.QToolBar()
# Actions
self.actionReply = self.ui.inboxContextMenuToolbar.addAction(_translate(
"MainWindow", "Reply to sender"), self.on_action_InboxReply)
@ -230,25 +230,22 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.tableWidgetInbox.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.ui.tableWidgetInbox.customContextMenuRequested.connect(
self.on_context_menuInbox)
self.ui.tableWidgetInboxSubscriptions.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.tableWidgetInboxSubscriptions, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.ui.tableWidgetInboxSubscriptions.customContextMenuRequested.connect(
self.on_context_menuInbox)
self.ui.tableWidgetInboxChans.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.tableWidgetInboxChans, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.ui.tableWidgetInboxChans.customContextMenuRequested.connect(
self.on_context_menuInbox)
def init_identities_popup_menu(self, connectSignal=True):
# Popup menu for the Your Identities tab
self.ui.addressContextMenuToolbarYourIdentities = QtGui.QToolBar()
self.ui.addressContextMenuToolbarYourIdentities = QtWidgets.QToolBar()
# Actions
self.actionNewYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction(_translate(
"MainWindow", "New"), self.on_action_YourIdentitiesNew)
@ -282,8 +279,7 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.treeWidgetYourIdentities.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.ui.treeWidgetYourIdentities.customContextMenuRequested.connect(
self.on_context_menuYourIdentities)
# load all gui.menu plugins with prefix 'address'
@ -332,13 +328,12 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.treeWidgetChans.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.treeWidgetChans, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.ui.treeWidgetChans.customContextMenuRequested.connect(
self.on_context_menuChan)
def init_addressbook_popup_menu(self, connectSignal=True):
# Popup menu for the Address Book page
self.ui.addressBookContextMenuToolbar = QtGui.QToolBar()
self.ui.addressBookContextMenuToolbar = QtWidgets.QToolBar()
# Actions
self.actionAddressBookSend = self.ui.addressBookContextMenuToolbar.addAction(
_translate(
@ -369,8 +364,7 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.tableWidgetAddressBook.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.ui.tableWidgetAddressBook.customContextMenuRequested.connect(
self.on_context_menuAddressBook)
def init_subscriptions_popup_menu(self, connectSignal=True):
@ -398,8 +392,7 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.treeWidgetSubscriptions.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.ui.treeWidgetSubscriptions.customContextMenuRequested.connect(
self.on_context_menuSubscriptions)
def init_sent_popup_menu(self, connectSignal=True):
@ -417,14 +410,14 @@ class MyForm(settingsmixin.SMainWindow):
self.actionSentReply = self.ui.sentContextMenuToolbar.addAction(
_translate("MainWindow", "Send update"),
self.on_action_SentReply)
# self.popMenuSent = QtGui.QMenu( self )
# self.popMenuSent = QtWidgets.QMenu( self )
# self.popMenuSent.addAction( self.actionSentClipboard )
# self.popMenuSent.addAction( self.actionTrashSentMessage )
def rerenderTabTreeSubscriptions(self):
treeWidget = self.ui.treeWidgetSubscriptions
folders = Ui_FolderWidget.folderWeight.keys()
folders.remove("new")
Ui_FolderWidget.folderWeight.pop("new", None)
# sort ascending when creating
if treeWidget.topLevelItemCount() == 0:
@ -441,7 +434,6 @@ class MyForm(settingsmixin.SMainWindow):
if treeWidget.isSortingEnabled():
treeWidget.setSortingEnabled(False)
widgets = {}
i = 0
while i < treeWidget.topLevelItemCount():
widget = treeWidget.topLevelItem(i)
@ -459,7 +451,8 @@ class MyForm(settingsmixin.SMainWindow):
while j < widget.childCount():
subwidget = widget.child(j)
try:
subwidget.setUnreadCount(db[toAddress][subwidget.folderName]['count'])
subwidget.setUnreadCount(
db[toAddress][subwidget.folderName]['count'])
unread += db[toAddress][subwidget.folderName]['count']
db[toAddress].pop(subwidget.folderName, None)
except:
@ -471,9 +464,10 @@ class MyForm(settingsmixin.SMainWindow):
# add missing folders
if len(db[toAddress]) > 0:
j = 0
for f, c in db[toAddress].iteritems():
for f, c in six.iteritems(db[toAddress]):
try:
subwidget = Ui_FolderWidget(widget, j, toAddress, f, c['count'])
subwidget = Ui_FolderWidget(
widget, j, toAddress, f, c['count'])
except KeyError:
subwidget = Ui_FolderWidget(widget, j, toAddress, f, 0)
j += 1
@ -484,20 +478,20 @@ class MyForm(settingsmixin.SMainWindow):
i = 0
for toAddress in db:
widget = Ui_SubscriptionWidget(
treeWidget,
i,
toAddress,
db[toAddress]["inbox"]['count'],
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'])
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)
subwidget = Ui_FolderWidget(
widget, j, toAddress, folder, 0)
j += 1
widget.setUnreadCount(unread)
i += 1
@ -530,8 +524,8 @@ class MyForm(settingsmixin.SMainWindow):
toAddress, 'enabled')
isChan = config.safeGetBoolean(
toAddress, 'chan')
isMaillinglist = config.safeGetBoolean(
toAddress, 'mailinglist')
# isMaillinglist = config.safeGetBoolean(
# toAddress, 'mailinglist')
if treeWidget == self.ui.treeWidgetYourIdentities:
if isChan:
@ -555,6 +549,8 @@ class MyForm(settingsmixin.SMainWindow):
"GROUP BY toaddress, folder")
for row in queryreturn:
toaddress, folder, cnt = row
toaddress = toaddress.decode("utf-8", "replace")
folder = folder.decode("utf-8", "replace")
total += cnt
if toaddress in db and folder in db[toaddress]:
db[toaddress][folder] = cnt
@ -569,7 +565,6 @@ class MyForm(settingsmixin.SMainWindow):
if treeWidget.isSortingEnabled():
treeWidget.setSortingEnabled(False)
widgets = {}
i = 0
while i < treeWidget.topLevelItemCount():
widget = treeWidget.topLevelItem(i)
@ -601,7 +596,7 @@ class MyForm(settingsmixin.SMainWindow):
# add missing folders
if len(db[toAddress]) > 0:
j = 0
for f, c in db[toAddress].iteritems():
for f, c in six.iteritems(db[toAddress]):
if toAddress is not None and tab == 'messages' and folder == "new":
continue
subwidget = Ui_FolderWidget(widget, j, toAddress, f, c)
@ -630,7 +625,7 @@ class MyForm(settingsmixin.SMainWindow):
treeWidget.setSortingEnabled(True)
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
super(MyForm, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
@ -649,12 +644,12 @@ class MyForm(settingsmixin.SMainWindow):
if addressVersionNumber == 1:
displayMsg = _translate(
"MainWindow",
"One of your addresses, %1, is an old version 1 address. "
"Version 1 addresses are no longer supported. "
"May we delete it now?").arg(addressInKeysFile)
reply = QtGui.QMessageBox.question(
self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
"One of your addresses, {0}, is an old version 1"
" address. Version 1 addresses are no longer supported."
" May we delete it now?").format(addressInKeysFile)
reply = QtWidgets.QMessageBox.question(
self, 'Message', displayMsg, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
config.remove_section(addressInKeysFile)
config.save()
@ -698,111 +693,97 @@ class MyForm(settingsmixin.SMainWindow):
self.rerenderSubscriptions()
# Initialize the inbox search
QtCore.QObject.connect(self.ui.inboxSearchLineEdit, QtCore.SIGNAL(
"returnPressed()"), self.inboxSearchLineEditReturnPressed)
QtCore.QObject.connect(self.ui.inboxSearchLineEditSubscriptions, QtCore.SIGNAL(
"returnPressed()"), self.inboxSearchLineEditReturnPressed)
QtCore.QObject.connect(self.ui.inboxSearchLineEditChans, QtCore.SIGNAL(
"returnPressed()"), self.inboxSearchLineEditReturnPressed)
QtCore.QObject.connect(self.ui.inboxSearchLineEdit, QtCore.SIGNAL(
"textChanged(QString)"), self.inboxSearchLineEditUpdated)
QtCore.QObject.connect(self.ui.inboxSearchLineEditSubscriptions, QtCore.SIGNAL(
"textChanged(QString)"), self.inboxSearchLineEditUpdated)
QtCore.QObject.connect(self.ui.inboxSearchLineEditChans, QtCore.SIGNAL(
"textChanged(QString)"), self.inboxSearchLineEditUpdated)
for line_edit in (
self.ui.inboxSearchLineEdit,
self.ui.inboxSearchLineEditSubscriptions,
self.ui.inboxSearchLineEditChans,
):
line_edit.returnPressed.connect(
self.inboxSearchLineEditReturnPressed)
line_edit.textChanged.connect(
self.inboxSearchLineEditUpdated)
# Initialize addressbook
QtCore.QObject.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL(
"itemChanged(QTableWidgetItem *)"), self.tableWidgetAddressBookItemChanged)
self.ui.tableWidgetAddressBook.itemChanged.connect(
self.tableWidgetAddressBookItemChanged)
# This is necessary for the completer to work if multiple recipients
QtCore.QObject.connect(self.ui.lineEditTo, QtCore.SIGNAL(
"cursorPositionChanged(int, int)"), self.ui.lineEditTo.completer().onCursorPositionChanged)
self.ui.lineEditTo.cursorPositionChanged.connect(
self.ui.lineEditTo.completer().onCursorPositionChanged)
# show messages from message list
QtCore.QObject.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL(
"itemSelectionChanged ()"), self.tableWidgetInboxItemClicked)
QtCore.QObject.connect(self.ui.tableWidgetInboxSubscriptions, QtCore.SIGNAL(
"itemSelectionChanged ()"), self.tableWidgetInboxItemClicked)
QtCore.QObject.connect(self.ui.tableWidgetInboxChans, QtCore.SIGNAL(
"itemSelectionChanged ()"), self.tableWidgetInboxItemClicked)
for table_widget in (
self.ui.tableWidgetInbox,
self.ui.tableWidgetInboxSubscriptions,
self.ui.tableWidgetInboxChans
):
table_widget.itemSelectionChanged.connect(
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)
QtCore.QObject.connect(
self.ui.tabWidget, QtCore.SIGNAL("currentChanged(int)"),
self.tabWidgetCurrentChanged
)
for tree_widget in (
self.ui.treeWidgetYourIdentities,
self.ui.treeWidgetSubscriptions,
self.ui.treeWidgetChans
):
tree_widget.itemSelectionChanged.connect(
self.treeWidgetItemClicked)
tree_widget.itemChanged.connect(self.treeWidgetItemChanged)
self.ui.tabWidget.currentChanged.connect(self.tabWidgetCurrentChanged)
# Put the colored icon on the status bar
# self.pushButtonStatusIcon.setIcon(QIcon(":/newPrefix/images/yellowicon.png"))
self.setStatusBar(BMStatusBar())
self.statusbar = self.statusBar()
self.pushButtonStatusIcon = QtGui.QPushButton(self)
self.pushButtonStatusIcon = QtWidgets.QPushButton(self)
self.pushButtonStatusIcon.setText('')
self.pushButtonStatusIcon.setIcon(
QtGui.QIcon(':/newPrefix/images/redicon.png'))
self.pushButtonStatusIcon.setFlat(True)
self.statusbar.insertPermanentWidget(0, self.pushButtonStatusIcon)
QtCore.QObject.connect(self.pushButtonStatusIcon, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonStatusIcon)
self.pushButtonStatusIcon.clicked.connect(
self.click_pushButtonStatusIcon)
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.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))
for widget in (
self.ui.tableWidgetInbox, self.ui.treeWidgetChans,
self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions,
self.ui.tableWidgetAddressBook
):
widget.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.UISignalThread = UISignaler.get()
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.writeNewAddressToTable)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateStatusBar(PyQt_PyObject)"), self.updateStatusBar)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateSentItemStatusByToAddress(PyQt_PyObject,PyQt_PyObject)"), self.updateSentItemStatusByToAddress)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"), self.updateSentItemStatusByAckdata)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
self.UISignalThread.writeNewAddressToTable.connect(
self.writeNewAddressToTable)
self.UISignalThread.updateStatusBar.connect(self.updateStatusBar)
self.UISignalThread.updateSentItemStatusByToAddress.connect(
self.updateSentItemStatusByToAddress)
self.UISignalThread.updateSentItemStatusByAckdata.connect(
self.updateSentItemStatusByAckdata)
self.UISignalThread.displayNewInboxMessage.connect(
self.displayNewInboxMessage)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,"
"PyQt_PyObject,PyQt_PyObject)"),
self.UISignalThread.displayNewSentMessage.connect(
self.displayNewSentMessage)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"setStatusIcon(PyQt_PyObject)"), self.setStatusIcon)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"changedInboxUnread(PyQt_PyObject)"), self.changedInboxUnread)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"rerenderMessagelistFromLabels()"), self.rerenderMessagelistFromLabels)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"rerenderMessgelistToLabels()"), self.rerenderMessagelistToLabels)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"rerenderAddressBook()"), self.rerenderAddressBook)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"rerenderSubscriptions()"), self.rerenderSubscriptions)
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.setStatusIcon.connect(self.setStatusIcon)
self.UISignalThread.changedInboxUnread.connect(self.changedInboxUnread)
self.UISignalThread.rerenderMessagelistFromLabels.connect(
self.rerenderMessagelistFromLabels)
self.UISignalThread.rerenderMessagelistToLabels.connect(
self.rerenderMessagelistToLabels)
self.UISignalThread.rerenderAddressBook.connect(
self.rerenderAddressBook)
self.UISignalThread.rerenderSubscriptions.connect(
self.rerenderSubscriptions)
self.UISignalThread.removeInboxRowByMsgid.connect(
self.removeInboxRowByMsgid)
self.UISignalThread.newVersionAvailable.connect(
self.newVersionAvailable)
self.UISignalThread.displayAlert.connect(self.displayAlert)
self.UISignalThread.start()
# Key press in tree view
@ -833,14 +814,13 @@ class MyForm(settingsmixin.SMainWindow):
TTL = config.getint('bitmessagesettings', 'ttl')
if TTL < 3600: # an hour
TTL = 3600
elif TTL > 28*24*60*60: # 28 days
TTL = 28*24*60*60
self.ui.horizontalSliderTTL.setSliderPosition((TTL - 3600) ** (1/3.199))
elif TTL > 28 * 24 * 60 * 60: # 28 days
TTL = 28 * 24 * 60 * 60
self.ui.horizontalSliderTTL.setSliderPosition(
int((TTL - 3600) ** (1 / 3.199)))
self.updateHumanFriendlyTTLDescription(TTL)
QtCore.QObject.connect(self.ui.horizontalSliderTTL, QtCore.SIGNAL(
"valueChanged(int)"), self.updateTTL)
self.ui.horizontalSliderTTL.valueChanged.connect(self.updateTTL)
self.initSettings()
self.resetNamecoinConnection()
self.sqlInit()
@ -895,27 +875,25 @@ class MyForm(settingsmixin.SMainWindow):
config.save()
def updateHumanFriendlyTTLDescription(self, TTL):
numberOfHours = int(round(TTL / (60*60)))
numberOfHours = int(round(TTL / (60 * 60)))
font = QtGui.QFont()
stylesheet = ""
if numberOfHours < 48:
self.ui.labelHumanFriendlyTTLDescription.setText(
_translate("MainWindow", "%n hour(s)", None, QtCore.QCoreApplication.CodecForTr, numberOfHours) +
", " +
_translate("MainWindow", "not recommended for chans", None, QtCore.QCoreApplication.CodecForTr)
_translate(
"MainWindow", "%n hour(s)", None, numberOfHours
) + ",\n" +
_translate("MainWindow", "not recommended for chans")
)
stylesheet = "QLabel { color : red; }"
font.setBold(True)
else:
numberOfDays = int(round(TTL / (24*60*60)))
numberOfDays = int(round(TTL / (24 * 60 * 60)))
self.ui.labelHumanFriendlyTTLDescription.setText(
_translate(
"MainWindow",
"%n day(s)",
None,
QtCore.QCoreApplication.CodecForTr,
numberOfDays))
"MainWindow", "%n day(s)", None, numberOfDays)
)
font.setBold(False)
self.ui.labelHumanFriendlyTTLDescription.setStyleSheet(stylesheet)
self.ui.labelHumanFriendlyTTLDescription.setFont(font)
@ -1026,7 +1004,7 @@ class MyForm(settingsmixin.SMainWindow):
# related = related.findItems(msgid, QtCore.Qt.MatchExactly),
# returns an empty list
for rrow in range(related.rowCount()):
if related.item(rrow, 3).data() == msgid:
if as_msgid(related.item(rrow, 3).data()) == msgid:
break
for col in range(widget.columnCount()):
@ -1047,6 +1025,8 @@ class MyForm(settingsmixin.SMainWindow):
normalUnread = {}
broadcastsUnread = {}
for addr, fld, count in queryReturn:
addr = addr.decode("utf-8", "replace")
fld = fld.decode("utf-8", "replace")
try:
normalUnread[addr][fld] = count
except KeyError:
@ -1066,8 +1046,10 @@ class MyForm(settingsmixin.SMainWindow):
queryReturn = sqlQuery(
'SELECT fromaddress, folder, COUNT(msgid) AS cnt'
' FROM inbox WHERE read = 0 AND toaddress = ?'
' GROUP BY fromaddress, folder', str_broadcast_subscribers)
' GROUP BY fromaddress, folder', dbstr(str_broadcast_subscribers))
for addr, fld, count in queryReturn:
addr = addr.decode("utf-8", "replace")
fld = fld.decode("utf-8", "replace")
try:
broadcastsUnread[addr][fld] = count
except KeyError:
@ -1078,15 +1060,15 @@ class MyForm(settingsmixin.SMainWindow):
for i in range(root.childCount()):
addressItem = root.child(i)
if addressItem.type == AccountMixin.ALL:
newCount = sum(totalUnread.itervalues())
newCount = sum(six.itervalues(totalUnread))
self.drawTrayIcon(self.currentTrayIconFileName, newCount)
else:
try:
newCount = sum((
newCount = sum(six.itervalues((
broadcastsUnread
if addressItem.type == AccountMixin.SUBSCRIPTION
else normalUnread
)[addressItem.address].itervalues())
)[addressItem.address]))
except KeyError:
newCount = 0
if newCount != addressItem.unreadCount:
@ -1143,20 +1125,19 @@ class MyForm(settingsmixin.SMainWindow):
elif status == 'msgsent':
statusText = _translate(
"MainWindow",
"Message sent. Waiting for acknowledgement. Sent at %1"
).arg(l10n.formatTimestamp(lastactiontime))
"Message sent. Waiting for acknowledgement. Sent at {0}"
).format(l10n.formatTimestamp(lastactiontime))
elif status == 'msgsentnoackexpected':
statusText = _translate(
"MainWindow", "Message sent. Sent at %1"
).arg(l10n.formatTimestamp(lastactiontime))
"MainWindow", "Message sent. Sent at {0}"
).format(l10n.formatTimestamp(lastactiontime))
elif status == 'doingmsgpow':
statusText = _translate(
"MainWindow", "Doing work necessary to send message.")
elif status == 'ackreceived':
statusText = _translate(
"MainWindow",
"Acknowledgement of the message received %1"
).arg(l10n.formatTimestamp(lastactiontime))
"MainWindow", "Acknowledgement of the message received {0}"
).format(l10n.formatTimestamp(lastactiontime))
elif status == 'broadcastqueued':
statusText = _translate(
"MainWindow", "Broadcast queued.")
@ -1164,36 +1145,36 @@ class MyForm(settingsmixin.SMainWindow):
statusText = _translate(
"MainWindow", "Doing work necessary to send broadcast.")
elif status == 'broadcastsent':
statusText = _translate("MainWindow", "Broadcast on %1").arg(
statusText = _translate("MainWindow", "Broadcast on {0}").format(
l10n.formatTimestamp(lastactiontime))
elif status == 'toodifficult':
statusText = _translate(
"MainWindow",
"Problem: The work demanded by the recipient is more"
" difficult than you are willing to do. %1"
).arg(l10n.formatTimestamp(lastactiontime))
" difficult than you are willing to do. {0}"
).format(l10n.formatTimestamp(lastactiontime))
elif status == 'badkey':
statusText = _translate(
"MainWindow",
"Problem: The recipient\'s encryption key is no good."
" Could not encrypt message. %1"
).arg(l10n.formatTimestamp(lastactiontime))
" Could not encrypt message. {0}"
).format(l10n.formatTimestamp(lastactiontime))
elif status == 'forcepow':
statusText = _translate(
"MainWindow",
"Forced difficulty override. Send should start soon.")
else:
statusText = _translate(
"MainWindow", "Unknown status: %1 %2").arg(status).arg(
"MainWindow", "Unknown status: {0} {1}").format(status,
l10n.formatTimestamp(lastactiontime))
items = [
MessageList_AddressWidget(
toAddress, unicode(acct.toLabel, 'utf-8')),
toAddress, unic(ustr(acct.toLabel))),
MessageList_AddressWidget(
fromAddress, unicode(acct.fromLabel, 'utf-8')),
fromAddress, unic(ustr(acct.fromLabel))),
MessageList_SubjectWidget(
str(subject), unicode(acct.subject, 'utf-8', 'replace')),
ustr(subject), unic(ustr(acct.subject))),
MessageList_TimeWidget(
statusText, False, lastactiontime, ackdata)]
self.addMessageListItem(tableWidget, items)
@ -1214,11 +1195,11 @@ class MyForm(settingsmixin.SMainWindow):
items = [
MessageList_AddressWidget(
toAddress, unicode(acct.toLabel, 'utf-8'), not read),
toAddress, unic(ustr(acct.toLabel)), not read),
MessageList_AddressWidget(
fromAddress, unicode(acct.fromLabel, 'utf-8'), not read),
fromAddress, unic(ustr(acct.fromLabel)), not read),
MessageList_SubjectWidget(
str(subject), unicode(acct.subject, 'utf-8', 'replace'),
ustr(subject), unic(ustr(acct.subject)),
not read),
MessageList_TimeWidget(
l10n.formatTimestamp(received), not read, received, msgid)
@ -1246,7 +1227,14 @@ class MyForm(settingsmixin.SMainWindow):
xAddress, account, "sent", where, what, False)
for row in queryreturn:
self.addMessageListItemSent(tableWidget, *row)
r = []
r.append(row[0].decode("utf-8", "replace")) # toaddress
r.append(row[1].decode("utf-8", "replace")) # fromaddress
r.append(row[2].decode("utf-8", "replace")) # subject
r.append(row[3].decode("utf-8", "replace")) # status
r.append(row[4]) # ackdata
r.append(row[5]) # lastactiontime
self.addMessageListItemSent(tableWidget, *r)
tableWidget.horizontalHeader().setSortIndicator(
3, QtCore.Qt.DescendingOrder)
@ -1287,6 +1275,10 @@ class MyForm(settingsmixin.SMainWindow):
for row in queryreturn:
toAddress, fromAddress, subject, _, msgid, received, read = row
toAddress = toAddress.decode("utf-8", "replace")
fromAddress = fromAddress.decode("utf-8", "replace")
subject = subject.decode("utf-8", "replace")
received = received.decode("utf-8", "replace")
self.addMessageListItemInbox(
tableWidget, toAddress, fromAddress, subject,
msgid, received, read)
@ -1302,23 +1294,21 @@ class MyForm(settingsmixin.SMainWindow):
# create application indicator
def appIndicatorInit(self, app):
self.initTrayIcon("can-icon-24px-red.png", app)
traySignal = "activated(QSystemTrayIcon::ActivationReason)"
QtCore.QObject.connect(self.tray, QtCore.SIGNAL(
traySignal), self.__icon_activated)
self.tray.activated.connect(self.__icon_activated)
m = QtGui.QMenu()
m = QtWidgets.QMenu()
self.actionStatus = QtGui.QAction(_translate(
self.actionStatus = QtWidgets.QAction(_translate(
"MainWindow", "Not Connected"), m, checkable=False)
m.addAction(self.actionStatus)
# separator
actionSeparator = QtGui.QAction('', m, checkable=False)
actionSeparator = QtWidgets.QAction('', m, checkable=False)
actionSeparator.setSeparator(True)
m.addAction(actionSeparator)
# show bitmessage
self.actionShow = QtGui.QAction(_translate(
self.actionShow = QtWidgets.QAction(_translate(
"MainWindow", "Show Bitmessage"), m, checkable=True)
self.actionShow.setChecked(not config.getboolean(
'bitmessagesettings', 'startintray'))
@ -1327,7 +1317,7 @@ class MyForm(settingsmixin.SMainWindow):
m.addAction(self.actionShow)
# quiet mode
self.actionQuiet = QtGui.QAction(_translate(
self.actionQuiet = QtWidgets.QAction(_translate(
"MainWindow", "Quiet Mode"), m, checkable=True)
self.actionQuiet.setChecked(not config.getboolean(
'bitmessagesettings', 'showtraynotifications'))
@ -1335,25 +1325,25 @@ class MyForm(settingsmixin.SMainWindow):
m.addAction(self.actionQuiet)
# Send
actionSend = QtGui.QAction(_translate(
actionSend = QtWidgets.QAction(_translate(
"MainWindow", "Send"), m, checkable=False)
actionSend.triggered.connect(self.appIndicatorSend)
m.addAction(actionSend)
# Subscribe
actionSubscribe = QtGui.QAction(_translate(
actionSubscribe = QtWidgets.QAction(_translate(
"MainWindow", "Subscribe"), m, checkable=False)
actionSubscribe.triggered.connect(self.appIndicatorSubscribe)
m.addAction(actionSubscribe)
# Channels
actionSubscribe = QtGui.QAction(_translate(
actionSubscribe = QtWidgets.QAction(_translate(
"MainWindow", "Channel"), m, checkable=False)
actionSubscribe.triggered.connect(self.appIndicatorChannel)
m.addAction(actionSubscribe)
# separator
actionSeparator = QtGui.QAction('', m, checkable=False)
actionSeparator = QtWidgets.QAction('', m, checkable=False)
actionSeparator.setSeparator(True)
m.addAction(actionSeparator)
@ -1372,6 +1362,7 @@ class MyForm(settingsmixin.SMainWindow):
SELECT msgid, toaddress, read FROM inbox where folder='inbox'
''')
for msgid, toAddress, read in queryreturn:
toAddress = toAddress.decode("utf-8", "replace")
if not read:
# increment the unread subscriptions if True (1)
@ -1450,7 +1441,7 @@ class MyForm(settingsmixin.SMainWindow):
# Adapters and converters for QT <-> sqlite
def sqlInit(self):
register_adapter(QtCore.QByteArray, str)
register_adapter(QtCore.QByteArray, bytes)
def indicatorInit(self):
"""
@ -1470,11 +1461,10 @@ class MyForm(settingsmixin.SMainWindow):
def notifierInit(self):
def _simple_notify(
title, subtitle, category, label=None, icon=None):
self.tray.showMessage(title, subtitle, 1, 2000)
self.tray.showMessage(
title, subtitle, self.tray.NoIcon, msecs=2000)
self._notifier = _simple_notify
# does nothing if isAvailable returns false
self._player = QtGui.QSound.play
if not get_plugins:
return
@ -1487,7 +1477,10 @@ class MyForm(settingsmixin.SMainWindow):
self._theme_player = get_plugin('notification.sound', 'theme')
if not QtGui.QSound.isAvailable():
try:
from qtpy import QtMultimedia
self._player = QtMultimedia.QSound.play
except ImportError:
_plugin = get_plugin(
'notification.sound', 'file', fallback='file.fallback')
if _plugin:
@ -1499,7 +1492,7 @@ class MyForm(settingsmixin.SMainWindow):
self, title, subtitle, category, label=None, icon=None):
self.playSound(category, label)
self._notifier(
unicode(title), unicode(subtitle), category, label, icon)
unic(ustr(title)), unic(ustr(subtitle)), category, label, icon)
# tree
def treeWidgetKeyPressEvent(self, event):
@ -1511,7 +1504,7 @@ class MyForm(settingsmixin.SMainWindow):
if event.key() == QtCore.Qt.Key_Delete:
self.on_action_AddressBookDelete()
else:
return QtGui.QTableWidget.keyPressEvent(
return QtWidgets.QTableWidget.keyPressEvent(
self.ui.tableWidgetAddressBook, event)
# inbox / sent
@ -1526,14 +1519,14 @@ class MyForm(settingsmixin.SMainWindow):
"""This method handles keypress events for all widgets on MyForm"""
messagelist = self.getCurrentMessagelist()
if event.key() == QtCore.Qt.Key_Delete:
if isinstance(focus, (MessageView, QtGui.QTableWidget)):
if isinstance(focus, (MessageView, QtWidgets.QTableWidget)):
folder = self.getCurrentFolder()
if folder == "sent":
self.on_action_SentTrash()
else:
self.on_action_InboxTrash()
event.ignore()
elif QtGui.QApplication.queryKeyboardModifiers() == QtCore.Qt.NoModifier:
elif QtWidgets.QApplication.queryKeyboardModifiers() == QtCore.Qt.NoModifier:
if event.key() == QtCore.Qt.Key_N:
currentRow = messagelist.currentRow()
if currentRow < messagelist.rowCount() - 1:
@ -1572,74 +1565,77 @@ class MyForm(settingsmixin.SMainWindow):
return
if isinstance(focus, MessageView):
return MessageView.keyPressEvent(focus, event)
if isinstance(focus, QtGui.QTableWidget):
return QtGui.QTableWidget.keyPressEvent(focus, event)
if isinstance(focus, QtGui.QTreeWidget):
return QtGui.QTreeWidget.keyPressEvent(focus, event)
elif isinstance(focus, QtWidgets.QTableWidget):
return QtWidgets.QTableWidget.keyPressEvent(focus, event)
elif isinstance(focus, QtWidgets.QTreeWidget):
return QtWidgets.QTreeWidget.keyPressEvent(focus, event)
# menu button 'manage keys'
def click_actionManageKeys(self):
if 'darwin' in sys.platform or 'linux' in sys.platform:
if state.appdata == '':
# reply = QtGui.QMessageBox.information(self, 'keys.dat?','You
# reply = QtWidgets.QMessageBox.information(self, 'keys.dat?','You
# may manage your keys by editing the keys.dat file stored in
# the same directory as this program. It is important that you
# back up this file.', QMessageBox.Ok)
reply = QtGui.QMessageBox.information(
self,
'keys.dat?',
_translate(
reply = QtWidgets.QMessageBox.information(
self, 'keys.dat?', _translate(
"MainWindow",
"You may manage your keys by editing the keys.dat file stored in the same directory"
"as this program. It is important that you back up this file."
),
QtGui.QMessageBox.Ok)
"You may manage your keys by editing the keys.dat"
" file stored in the same directory as this"
" program. It is important that you back up this"
" file."), QtWidgets.QMessageBox.Ok)
else:
QtGui.QMessageBox.information(
self,
'keys.dat?',
QtWidgets.QMessageBox.information(
self, 'keys.dat?',
_translate(
"MainWindow",
"You may manage your keys by editing the keys.dat file stored in"
"\n %1 \n"
"It is important that you back up this file."
).arg(state.appdata),
QtGui.QMessageBox.Ok)
"You may manage your keys by editing the keys.dat"
" file stored in\n {0} \nIt is important that you"
" back up this file."
).format(state.appdata),
QtWidgets.QMessageBox.Ok
)
elif sys.platform == 'win32' or sys.platform == 'win64':
if state.appdata == '':
reply = QtGui.QMessageBox.question(
self,
_translate("MainWindow", "Open keys.dat?"),
reply = QtWidgets.QMessageBox.question(
self, _translate("MainWindow", "Open keys.dat?"),
_translate(
"MainWindow",
"You may manage your keys by editing the keys.dat file stored in the same directory as "
"this program. It is important that you back up this file. "
"Would you like to open the file now? "
"(Be sure to close Bitmessage before making any changes.)"),
QtGui.QMessageBox.Yes,
QtGui.QMessageBox.No)
"You may manage your keys by editing the keys.dat"
" file stored in the same directory as this"
" program. It is important that you back up this"
" file. Would you like to open the file now?"
" (Be sure to close Bitmessage before making any"
" changes.)"
), QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
else:
reply = QtGui.QMessageBox.question(
reply = QtWidgets.QMessageBox.question(
self,
_translate("MainWindow", "Open keys.dat?"),
_translate(
"MainWindow",
"You may manage your keys by editing the keys.dat file stored in\n %1 \n"
"It is important that you back up this file. Would you like to open the file now?"
"(Be sure to close Bitmessage before making any changes.)").arg(state.appdata),
QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
"You may manage your keys by editing the keys.dat"
" file stored in\n {0} \nIt is important that you"
" back up this file. Would you like to open the"
" file now? (Be sure to close Bitmessage before"
" making any changes.)"
).format(state.appdata),
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No
)
if reply == QtWidgets.QMessageBox.Yes:
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:
if QtWidgets.QMessageBox.question(
self, _translate("MainWindow", "Delete trash?"),
_translate(
"MainWindow",
"Are you sure you want to delete all trashed messages?"
), QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No
) == QtWidgets.QMessageBox.No:
return
sqlStoredProcedure('deleteandvacuume')
self.rerenderTabTreeMessages()
@ -1666,7 +1662,7 @@ class MyForm(settingsmixin.SMainWindow):
dialog = dialogs.RegenerateAddressesDialog(self)
if dialog.exec_():
if dialog.lineEditPassphrase.text() == "":
QtGui.QMessageBox.about(
QtWidgets.QMessageBox.about(
self, _translate("MainWindow", "bad passphrase"),
_translate(
"MainWindow",
@ -1679,7 +1675,7 @@ class MyForm(settingsmixin.SMainWindow):
addressVersionNumber = int(
dialog.lineEditAddressVersionNumber.text())
except:
QtGui.QMessageBox.about(
QtWidgets.QMessageBox.about(
self,
_translate("MainWindow", "Bad address version number"),
_translate(
@ -1689,7 +1685,7 @@ class MyForm(settingsmixin.SMainWindow):
))
return
if addressVersionNumber < 3 or addressVersionNumber > 4:
QtGui.QMessageBox.about(
QtWidgets.QMessageBox.about(
self,
_translate("MainWindow", "Bad address version number"),
_translate(
@ -1702,7 +1698,7 @@ class MyForm(settingsmixin.SMainWindow):
addressVersionNumber, streamNumberForAddress,
"regenerated deterministic address",
dialog.spinBoxNumberOfAddressesToMake.value(),
dialog.lineEditPassphrase.text().toUtf8(),
ustr(dialog.lineEditPassphrase.text()).encode("utf-8", "replace"),
dialog.checkBoxEighteenByteRipe.isChecked()
))
self.ui.tabWidget.setCurrentIndex(
@ -1753,7 +1749,7 @@ class MyForm(settingsmixin.SMainWindow):
pass
def __icon_activated(self, reason):
if reason == QtGui.QSystemTrayIcon.Trigger:
if reason == QtWidgets.QSystemTrayIcon.Trigger:
self.actionShow.setChecked(not self.actionShow.isChecked())
self.appIndicatorShowOrHideWindow()
@ -1823,7 +1819,7 @@ class MyForm(settingsmixin.SMainWindow):
def initTrayIcon(self, iconFileName, app):
self.currentTrayIconFileName = iconFileName
self.tray = QtGui.QSystemTrayIcon(
self.tray = QtWidgets.QSystemTrayIcon(
self.calcTrayIcon(iconFileName, self.findInboxUnreadCount()), app)
def setTrayIconFile(self, iconFileName):
@ -1855,8 +1851,8 @@ class MyForm(settingsmixin.SMainWindow):
# draw text
painter = QtGui.QPainter()
painter.begin(pixmap)
painter.setPen(
QtGui.QPen(QtGui.QColor(255, 0, 0), QtCore.Qt.SolidPattern))
painter.setPen(QtGui.QPen(QtGui.QColor(255, 0, 0)))
painter.setBrush(QtCore.Qt.SolidPattern)
painter.setFont(font)
painter.drawText(24-rect.right()-marginX, -rect.top()+marginY, txt)
painter.end()
@ -1903,10 +1899,11 @@ class MyForm(settingsmixin.SMainWindow):
if toAddress == rowAddress:
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 = textToDisplay.find('\n')
# If someone misses adding a "_translate" to a string
# before passing it to this function
# ? why textToDisplay isn't unicode
except AttributeError:
newlinePosition = 0
if newlinePosition > 1:
sent.item(i, 3).setText(
@ -1915,8 +1912,6 @@ class MyForm(settingsmixin.SMainWindow):
sent.item(i, 3).setText(textToDisplay)
def updateSentItemStatusByAckdata(self, ackdata, textToDisplay):
if type(ackdata) is str:
ackdata = QtCore.QByteArray(ackdata)
for sent in (
self.ui.tableWidgetInbox,
self.ui.tableWidgetInboxSubscriptions,
@ -1926,22 +1921,22 @@ class MyForm(settingsmixin.SMainWindow):
if self.getCurrentFolder(treeWidget) != "sent":
continue
for i in range(sent.rowCount()):
toAddress = sent.item(i, 0).data(QtCore.Qt.UserRole)
tableAckdata = sent.item(i, 3).data()
status, addressVersionNumber, streamNumber, ripe = decodeAddress(
toAddress)
# toAddress = sent.item(i, 0).data(QtCore.Qt.UserRole)
tableAckdata = as_msgid(sent.item(i, 3).data())
# status, addressVersionNumber, streamNumber, ripe = decodeAddress(
# toAddress)
if ackdata == tableAckdata:
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 = textToDisplay.find('\n')
# If someone misses adding a "_translate" to a string
# before passing it to this function
# ? why textToDisplay isn't unicode
except AttributeError:
newlinePosition = 0
if newlinePosition > 1:
sent.item(i, 3).setText(
textToDisplay[:newlinePosition])
else:
textToDisplay = textToDisplay[:newlinePosition]
sent.item(i, 3).setText(textToDisplay)
def removeInboxRowByMsgid(self, msgid):
@ -1953,7 +1948,7 @@ class MyForm(settingsmixin.SMainWindow):
):
i = None
for i in range(inbox.rowCount()):
if msgid == inbox.item(i, 3).data():
if msgid == as_msgid(inbox.item(i, 3).data()):
break
else:
continue
@ -1970,14 +1965,15 @@ class MyForm(settingsmixin.SMainWindow):
self.notifiedNewVersion = ".".join(str(n) for n in version)
self.updateStatusBar(_translate(
"MainWindow",
"New version of PyBitmessage is available: %1. Download it"
"New version of PyBitmessage is available: {0}. Download it"
" from https://github.com/Bitmessage/PyBitmessage/releases/latest"
).arg(self.notifiedNewVersion)
).format(self.notifiedNewVersion)
)
def displayAlert(self, title, text, exitAfterUserClicksOk):
self.updateStatusBar(text)
QtGui.QMessageBox.critical(self, title, text, QtGui.QMessageBox.Ok)
QtWidgets.QMessageBox.critical(
self, title, text, QtWidgets.QMessageBox.Ok)
if exitAfterUserClicksOk:
os._exit(0)
@ -1996,11 +1992,11 @@ class MyForm(settingsmixin.SMainWindow):
messagelist.item(i, 0).setLabel()
def rerenderAddressBook(self):
def addRow (address, label, type):
def addRow(address, label, type):
self.ui.tableWidgetAddressBook.insertRow(0)
newItem = Ui_AddressBookWidgetItemLabel(address, unicode(label, 'utf-8'), type)
newItem = Ui_AddressBookWidgetItemLabel(address, unic(ustr(label)), type)
self.ui.tableWidgetAddressBook.setItem(0, 0, newItem)
newItem = Ui_AddressBookWidgetItemAddress(address, unicode(label, 'utf-8'), type)
newItem = Ui_AddressBookWidgetItemAddress(address, unic(ustr(label)), type)
self.ui.tableWidgetAddressBook.setItem(0, 1, newItem)
oldRows = {}
@ -2009,7 +2005,8 @@ class MyForm(settingsmixin.SMainWindow):
oldRows[item.address] = [item.label, item.type, i]
if self.ui.tableWidgetAddressBook.rowCount() == 0:
self.ui.tableWidgetAddressBook.horizontalHeader().setSortIndicator(0, QtCore.Qt.AscendingOrder)
self.ui.tableWidgetAddressBook.horizontalHeader(
).setSortIndicator(0, QtCore.Qt.AscendingOrder)
if self.ui.tableWidgetAddressBook.isSortingEnabled():
self.ui.tableWidgetAddressBook.setSortingEnabled(False)
@ -2018,16 +2015,23 @@ class MyForm(settingsmixin.SMainWindow):
queryreturn = sqlQuery('SELECT label, address FROM subscriptions WHERE enabled = 1')
for row in queryreturn:
label, address = row
label = label.decode("utf-8", "replace")
address = address.decode("utf-8", "replace")
newRows[address] = [label, AccountMixin.SUBSCRIPTION]
# chans
for address in config.addresses(True):
account = accountClass(address)
if (account.type == AccountMixin.CHAN and config.safeGetBoolean(address, 'enabled')):
if (
account.type == AccountMixin.CHAN
and config.safeGetBoolean(address, 'enabled')
):
newRows[address] = [account.getLabel(), AccountMixin.CHAN]
# normal accounts
queryreturn = sqlQuery('SELECT * FROM addressbook')
queryreturn = sqlQuery('SELECT label, address FROM addressbook')
for row in queryreturn:
label, address = row
label = label.decode("utf-8", "replace")
address = address.decode("utf-8", "replace")
newRows[address] = [label, AccountMixin.NORMAL]
completerList = []
@ -2041,7 +2045,7 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.tableWidgetAddressBook.removeRow(oldRows[address][2])
for address in newRows:
addRow(address, newRows[address][0], newRows[address][1])
completerList.append(unicode(newRows[address][0], encoding="UTF-8") + " <" + address + ">")
completerList.append(unic(ustr(newRows[address][0]) + " <" + ustr(address) + ">"))
# sort
self.ui.tableWidgetAddressBook.sortByColumn(
@ -2053,16 +2057,17 @@ class MyForm(settingsmixin.SMainWindow):
self.rerenderTabTreeSubscriptions()
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."""),
QtGui.QMessageBox.Ok)
QtWidgets.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."
), QtWidgets.QMessageBox.Ok)
def click_pushButtonClear(self):
self.ui.lineEditSubject.setText("")
@ -2071,7 +2076,10 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.comboBoxSendFrom.setCurrentIndex(0)
def click_pushButtonSend(self):
encoding = 3 if QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier else 2
# pylint: disable=too-many-locals
encoding = (
3 if QtWidgets.QApplication.queryKeyboardModifiers()
& QtCore.Qt.ShiftModifier else 2)
self.statusbar.clearMessage()
@ -2079,22 +2087,22 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.tabWidgetSend.indexOf(self.ui.sendDirect):
# message to specific people
sendMessageToPeople = True
fromAddress = str(self.ui.comboBoxSendFrom.itemData(
fromAddress = ustr(self.ui.comboBoxSendFrom.itemData(
self.ui.comboBoxSendFrom.currentIndex(),
QtCore.Qt.UserRole).toString())
toAddresses = str(self.ui.lineEditTo.text().toUtf8())
subject = str(self.ui.lineEditSubject.text().toUtf8())
message = str(
self.ui.textEditMessage.document().toPlainText().toUtf8())
QtCore.Qt.UserRole))
toAddresses = ustr(self.ui.lineEditTo.text())
subject = ustr(self.ui.lineEditSubject.text())
message = ustr(
self.ui.textEditMessage.document().toPlainText())
else:
# broadcast message
sendMessageToPeople = False
fromAddress = str(self.ui.comboBoxSendFromBroadcast.itemData(
fromAddress = ustr(self.ui.comboBoxSendFromBroadcast.itemData(
self.ui.comboBoxSendFromBroadcast.currentIndex(),
QtCore.Qt.UserRole).toString())
subject = str(self.ui.lineEditSubjectBroadcast.text().toUtf8())
message = str(
self.ui.textEditMessageBroadcast.document().toPlainText().toUtf8())
QtCore.Qt.UserRole))
subject = ustr(self.ui.lineEditSubjectBroadcast.text())
message = ustr(
self.ui.textEditMessageBroadcast.document().toPlainText())
"""
The whole network message must fit in 2^18 bytes.
Let's assume 500 bytes of overhead. If someone wants to get that
@ -2103,14 +2111,14 @@ class MyForm(settingsmixin.SMainWindow):
users can send messages of any length.
"""
if len(message) > (2 ** 18 - 500):
QtGui.QMessageBox.about(
QtWidgets.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"
" by {0} bytes. (The maximum is 261644 bytes). Please"
" cut it down before sending."
).arg(len(message) - (2 ** 18 - 500)))
).format(len(message) - (2 ** 18 - 500)))
return
acct = accountClass(fromAddress)
@ -2131,24 +2139,31 @@ class MyForm(settingsmixin.SMainWindow):
# email address
if toAddress.find("@") >= 0:
if isinstance(acct, GatewayAccount):
acct.createMessage(toAddress, fromAddress, subject, message)
acct.createMessage(
toAddress, fromAddress, subject, message)
subject = acct.subject
toAddress = acct.toAddress
else:
if QtGui.QMessageBox.question(
self,
"Sending an email?",
if QtWidgets.QMessageBox.question(
self, "Sending an email?",
_translate(
"MainWindow",
"You are trying to send an email instead of a bitmessage. "
"This requires registering with a gateway. Attempt to register?"),
QtGui.QMessageBox.Yes|QtGui.QMessageBox.No) != QtGui.QMessageBox.Yes:
"You are trying to send an email"
" instead of a bitmessage. This"
" requires registering with a"
" gateway. Attempt to register?"
), QtWidgets.QMessageBox.Yes
| QtWidgets.QMessageBox.No
) != QtWidgets.QMessageBox.Yes:
continue
email = acct.getLabel()
if email[-14:] != "@mailchuck.com": # attempt register
# attempt register
if email[-14:] != "@mailchuck.com":
# 12 character random email address
email = ''.join(
random.SystemRandom().choice(string.ascii_lowercase) for _ in range(12)
email = u''.join(
random.SystemRandom().choice(
string.ascii_lowercase
) for _ in range(12)
) + "@mailchuck.com"
acct = MailchuckAccount(fromAddress)
acct.register(email)
@ -2159,15 +2174,15 @@ class MyForm(settingsmixin.SMainWindow):
"MainWindow",
"Error: Your account wasn't registered at"
" an email gateway. Sending registration"
" now as %1, please wait for the registration"
" now as {0}, please wait for the registration"
" to be processed before retrying sending."
).arg(email)
)
).format(email))
return
status, addressVersionNumber, streamNumber = decodeAddress(toAddress)[:3]
status, addressVersionNumber, streamNumber = \
decodeAddress(toAddress)[:3]
if status != 'success':
try:
toAddress = unicode(toAddress, 'utf-8', 'ignore')
toAddress = unic(ustr(toAddress))
except:
pass
logger.error('Error: Could not decode recipient address ' + toAddress + ':' + status)
@ -2176,58 +2191,58 @@ class MyForm(settingsmixin.SMainWindow):
self.updateStatusBar(_translate(
"MainWindow",
"Error: Bitmessage addresses start with"
" BM- Please check the recipient address %1"
).arg(toAddress))
" BM- Please check the recipient address {0}"
).format(toAddress))
elif status == 'checksumfailed':
self.updateStatusBar(_translate(
"MainWindow",
"Error: The recipient address %1 is not"
"Error: The recipient address {0} is not"
" typed or copied correctly. Please check it."
).arg(toAddress))
).format(toAddress))
elif status == 'invalidcharacters':
self.updateStatusBar(_translate(
"MainWindow",
"Error: The recipient address %1 contains"
"Error: The recipient address {0} contains"
" invalid characters. Please check it."
).arg(toAddress))
).format(toAddress))
elif status == 'versiontoohigh':
self.updateStatusBar(_translate(
"MainWindow",
"Error: The version of the recipient address"
" %1 is too high. Either you need to upgrade"
" {0} is too high. Either you need to upgrade"
" your Bitmessage software or your"
" acquaintance is being clever."
).arg(toAddress))
).format(toAddress))
elif status == 'ripetooshort':
self.updateStatusBar(_translate(
"MainWindow",
"Error: Some data encoded in the recipient"
" address %1 is too short. There might be"
" address {0} is too short. There might be"
" something wrong with the software of"
" your acquaintance."
).arg(toAddress))
).format(toAddress))
elif status == 'ripetoolong':
self.updateStatusBar(_translate(
"MainWindow",
"Error: Some data encoded in the recipient"
" address %1 is too long. There might be"
" address {0} is too long. There might be"
" something wrong with the software of"
" your acquaintance."
).arg(toAddress))
).format(toAddress))
elif status == 'varintmalformed':
self.updateStatusBar(_translate(
"MainWindow",
"Error: Some data encoded in the recipient"
" address %1 is malformed. There might be"
" address {0} is malformed. There might be"
" something wrong with the software of"
" your acquaintance."
).arg(toAddress))
).format(toAddress))
else:
self.updateStatusBar(_translate(
"MainWindow",
"Error: Something is wrong with the"
" recipient address %1."
).arg(toAddress))
" recipient address {0}."
).format(toAddress))
elif fromAddress == '':
self.updateStatusBar(_translate(
"MainWindow",
@ -2239,24 +2254,31 @@ class MyForm(settingsmixin.SMainWindow):
toAddress = addBMIfNotPresent(toAddress)
if addressVersionNumber > 4 or addressVersionNumber <= 1:
QtGui.QMessageBox.about(
QtWidgets.QMessageBox.about(
self,
_translate("MainWindow", "Address version number"),
_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)))
"Concerning the address {0}, Bitmessage"
" cannot understand address version"
" numbers of {1}. Perhaps upgrade"
" Bitmessage to the latest version."
).format(toAddress, addressVersionNumber)
)
continue
if streamNumber > 1 or streamNumber == 0:
QtGui.QMessageBox.about(
QtWidgets.QMessageBox.about(
self,
_translate("MainWindow", "Stream number"),
_translate(
"MainWindow",
"Concerning the address %1, Bitmessage cannot handle stream numbers of %2."
" Perhaps upgrade Bitmessage to the latest version."
).arg(toAddress).arg(str(streamNumber)))
"Concerning the address {0}, Bitmessage"
" cannot handle stream numbers of {1}."
" Perhaps upgrade Bitmessage to the"
" latest version."
).format(toAddress, streamNumber)
)
continue
self.statusbar.clearMessage()
if state.statusIconColor == 'red':
@ -2272,10 +2294,11 @@ class MyForm(settingsmixin.SMainWindow):
subject=subject, message=message, encoding=encoding)
toLabel = ''
queryreturn = sqlQuery('''select label from addressbook where address=?''',
toAddress)
dbstr(toAddress))
if queryreturn != []:
for row in queryreturn:
toLabel, = row
toLabel = toLabel.decode("utf-8", "replace")
self.displayNewSentMessage(
toAddress, toLabel, fromAddress, subject, message, ackdata)
@ -2341,11 +2364,11 @@ class MyForm(settingsmixin.SMainWindow):
))
def click_pushButtonFetchNamecoinID(self):
identities = str(self.ui.lineEditTo.text().toUtf8()).split(";")
identities = ustr(self.ui.lineEditTo.text()).split(";")
err, addr = self.namecoin.query(identities[-1].strip())
if err is not None:
self.updateStatusBar(
_translate("MainWindow", "Error: %1").arg(err))
_translate("MainWindow", "Error: {0}").format(err))
else:
identities[-1] = addr
self.ui.lineEditTo.setText("; ".join(identities))
@ -2358,7 +2381,7 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.tabWidgetSend.setCurrentIndex(
self.ui.tabWidgetSend.indexOf(
self.ui.sendBroadcast
if config.safeGetBoolean(str(address), 'mailinglist')
if config.safeGetBoolean(ustr(address), 'mailinglist')
else self.ui.sendDirect
))
@ -2369,21 +2392,22 @@ class MyForm(settingsmixin.SMainWindow):
# It's easier for others to read.
isEnabled = config.getboolean(
addressInKeysFile, 'enabled')
isMaillinglist = config.safeGetBoolean(addressInKeysFile, 'mailinglist')
isMaillinglist = config.safeGetBoolean(
addressInKeysFile, 'mailinglist')
if isEnabled and not isMaillinglist:
label = unicode(config.get(addressInKeysFile, 'label'), 'utf-8', 'ignore').strip()
label = unic(ustr(config.get(addressInKeysFile, 'label')).strip()) or addressInKeysFile
if label == "":
label = addressInKeysFile
self.ui.comboBoxSendFrom.addItem(avatarize(addressInKeysFile), label, addressInKeysFile)
# self.ui.comboBoxSendFrom.model().sort(1, Qt.AscendingOrder)
for i in range(self.ui.comboBoxSendFrom.count()):
address = str(self.ui.comboBoxSendFrom.itemData(
i, QtCore.Qt.UserRole).toString())
address = ustr(self.ui.comboBoxSendFrom.itemData(
i, QtCore.Qt.UserRole))
self.ui.comboBoxSendFrom.setItemData(
i, AccountColor(address).accountColor(),
QtCore.Qt.ForegroundRole)
self.ui.comboBoxSendFrom.insertItem(0, '', '')
if(self.ui.comboBoxSendFrom.count() == 2):
if self.ui.comboBoxSendFrom.count() == 2:
self.ui.comboBoxSendFrom.setCurrentIndex(1)
else:
self.ui.comboBoxSendFrom.setCurrentIndex(0)
@ -2395,18 +2419,16 @@ class MyForm(settingsmixin.SMainWindow):
addressInKeysFile, 'enabled')
isChan = config.safeGetBoolean(addressInKeysFile, 'chan')
if isEnabled and not isChan:
label = unicode(config.get(addressInKeysFile, 'label'), 'utf-8', 'ignore').strip()
if label == "":
label = addressInKeysFile
label = unic(ustr(config.get(addressInKeysFile, 'label')).strip()) or addressInKeysFile
self.ui.comboBoxSendFromBroadcast.addItem(avatarize(addressInKeysFile), label, addressInKeysFile)
for i in range(self.ui.comboBoxSendFromBroadcast.count()):
address = str(self.ui.comboBoxSendFromBroadcast.itemData(
i, QtCore.Qt.UserRole).toString())
address = ustr(self.ui.comboBoxSendFromBroadcast.itemData(
i, QtCore.Qt.UserRole))
self.ui.comboBoxSendFromBroadcast.setItemData(
i, AccountColor(address).accountColor(),
QtCore.Qt.ForegroundRole)
self.ui.comboBoxSendFromBroadcast.insertItem(0, '', '')
if(self.ui.comboBoxSendFromBroadcast.count() == 2):
if self.ui.comboBoxSendFromBroadcast.count() == 2:
self.ui.comboBoxSendFromBroadcast.setCurrentIndex(1)
else:
self.ui.comboBoxSendFromBroadcast.setCurrentIndex(0)
@ -2480,6 +2502,8 @@ class MyForm(settingsmixin.SMainWindow):
tableWidget = self.widgetConvert(treeWidget)
current_account = self.getCurrentAccount(treeWidget)
current_folder = self.getCurrentFolder(treeWidget)
# inventoryHash surprisingly is of type unicode
# inventoryHash = inventoryHash.encode('utf-8')
# pylint: disable=too-many-boolean-expressions
if ((tableWidget == inbox
and current_account == acct.address
@ -2500,8 +2524,8 @@ class MyForm(settingsmixin.SMainWindow):
'bitmessagesettings', 'showtraynotifications'):
self.notifierShow(
_translate("MainWindow", "New Message"),
_translate("MainWindow", "From %1").arg(
unicode(acct.fromLabel, 'utf-8')),
_translate("MainWindow", "From {0}").format(
unic(ustr(acct.fromLabel))),
sound.SOUND_UNKNOWN
)
if self.getCurrentAccount() is not None and (
@ -2527,7 +2551,7 @@ class MyForm(settingsmixin.SMainWindow):
dialog.exec_()
try:
address, label = dialog.data
except AttributeError:
except (AttributeError, TypeError):
return
# First we must check to see if the address is already in the
@ -2560,7 +2584,7 @@ class MyForm(settingsmixin.SMainWindow):
# Add to database (perhaps this should be separated from the MyForm class)
sqlExecute(
'''INSERT INTO subscriptions VALUES (?,?,?)''',
label, address, True
dbstr(label), dbstr(address), True
)
self.rerenderMessagelistFromLabels()
shared.reloadBroadcastSendersForWhichImWatching()
@ -2572,7 +2596,7 @@ class MyForm(settingsmixin.SMainWindow):
dialog.exec_()
try:
address, label = dialog.data
except AttributeError:
except (AttributeError, TypeError):
return
# We must check to see if the address is already in the
@ -2631,15 +2655,14 @@ class MyForm(settingsmixin.SMainWindow):
dialog = dialogs.EmailGatewayDialog(self, config=config)
# For Modal dialogs
dialog.exec_()
try:
acct = dialog.data
except AttributeError:
if not acct:
return
# Only settings remain here
acct.settings()
for i in range(self.ui.comboBoxSendFrom.count()):
if str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) \
if ustr(self.ui.comboBoxSendFrom.itemData(i)) \
== acct.fromAddress:
self.ui.comboBoxSendFrom.setCurrentIndex(i)
break
@ -2658,13 +2681,13 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.textEditMessage.setFocus()
def on_action_MarkAllRead(self):
if QtGui.QMessageBox.question(
if QtWidgets.QMessageBox.question(
self, "Marking all messages as read?",
_translate(
"MainWindow",
"Are you sure you would like to mark all messages read?"
), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
) != QtGui.QMessageBox.Yes:
), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
) != QtWidgets.QMessageBox.Yes:
return
tableWidget = self.getCurrentMessagelist()
@ -2674,13 +2697,18 @@ class MyForm(settingsmixin.SMainWindow):
msgids = []
for i in range(0, idCount):
msgids.append(tableWidget.item(i, 3).data())
msgids.append(sqlite3.Binary(as_msgid(tableWidget.item(i, 3).data())))
for col in xrange(tableWidget.columnCount()):
tableWidget.item(i, col).setUnread(False)
markread = sqlExecuteChunked(
"UPDATE inbox SET read = 1 WHERE msgid IN({0}) AND read=0",
idCount, *msgids
False, idCount, *msgids
)
if markread < 1:
markread = sqlExecuteChunked(
"UPDATE inbox SET read = 1 WHERE msgid IN({0}) AND read=0",
True, idCount, *msgids
)
if markread > 0:
@ -2692,7 +2720,7 @@ class MyForm(settingsmixin.SMainWindow):
def network_switch(self):
dontconnect_option = not config.safeGetBoolean(
'bitmessagesettings', 'dontconnect')
reply = QtGui.QMessageBox.question(
reply = QtWidgets.QMessageBox.question(
self, _translate("MainWindow", "Disconnecting")
if dontconnect_option else _translate("MainWindow", "Connecting"),
_translate(
@ -2701,12 +2729,12 @@ class MyForm(settingsmixin.SMainWindow):
) if dontconnect_option else _translate(
"MainWindow",
"Bitmessage will now start connecting to network. Are you sure?"
), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel,
QtGui.QMessageBox.Cancel)
if reply != QtGui.QMessageBox.Yes:
), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel,
QtWidgets.QMessageBox.Cancel)
if reply != QtWidgets.QMessageBox.Yes:
return
config.set(
'bitmessagesettings', 'dontconnect', str(dontconnect_option))
'bitmessagesettings', 'dontconnect', ustr(dontconnect_option))
config.save()
self.ui.updateNetworkSwitchMenuLabel(dontconnect_option)
@ -2729,68 +2757,66 @@ class MyForm(settingsmixin.SMainWindow):
waitForSync = False
# C PoW currently doesn't support interrupting and OpenCL is untested
if getPowType() == "python" and (powQueueSize() > 0 or pendingUpload() > 0):
reply = QtGui.QMessageBox.question(
if getPowType() == "python" and (
powQueueSize() > 0 or pendingUpload() > 0):
reply = QtWidgets.QMessageBox.question(
self, _translate("MainWindow", "Proof of work pending"),
_translate(
"MainWindow",
"%n object(s) pending proof of work", None,
QtCore.QCoreApplication.CodecForTr, powQueueSize()
) + ", " +
_translate(
"%n object(s) pending proof of work", None, powQueueSize()
) + ", "
+ _translate(
"MainWindow",
"%n object(s) waiting to be distributed", None,
QtCore.QCoreApplication.CodecForTr, pendingUpload()
) + "\n\n" +
_translate(
"MainWindow", "Wait until these tasks finish?"),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
| QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel)
if reply == QtGui.QMessageBox.No:
"%n object(s) waiting to be distributed",
None, pendingUpload()
) + "\n\n"
+ _translate("MainWindow", "Wait until these tasks finish?"),
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
| QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel
)
if reply == QtWidgets.QMessageBox.No:
waitForPow = False
elif reply == QtGui.QMessageBox.Cancel:
elif reply == QtWidgets.QMessageBox.Cancel:
return
if pendingDownload() > 0:
reply = QtGui.QMessageBox.question(
reply = QtWidgets.QMessageBox.question(
self, _translate("MainWindow", "Synchronisation pending"),
_translate(
"MainWindow",
"Bitmessage hasn't synchronised with the network,"
" %n object(s) to be downloaded. If you quit now,"
" it may cause delivery delays. Wait until the"
" synchronisation finishes?", None,
QtCore.QCoreApplication.CodecForTr, pendingDownload()
),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
| QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel)
if reply == QtGui.QMessageBox.Yes:
" synchronisation finishes?",
None, pendingDownload()
), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
| QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
if reply == QtWidgets.QMessageBox.Yes:
self.wait = waitForSync = True
elif reply == QtGui.QMessageBox.Cancel:
elif reply == QtWidgets.QMessageBox.Cancel:
return
if state.statusIconColor == 'red' and not config.safeGetBoolean(
'bitmessagesettings', 'dontconnect'):
reply = QtGui.QMessageBox.question(
reply = QtWidgets.QMessageBox.question(
self, _translate("MainWindow", "Not connected"),
_translate(
"MainWindow",
"Bitmessage isn't connected to the network. If you"
" quit now, it may cause delivery delays. Wait until"
" connected and the synchronisation finishes?"
),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
| QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel)
if reply == QtGui.QMessageBox.Yes:
), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
| QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
if reply == QtWidgets.QMessageBox.Yes:
waitForConnection = True
self.wait = waitForSync = True
elif reply == QtGui.QMessageBox.Cancel:
elif reply == QtWidgets.QMessageBox.Cancel:
return
self.quitAccepted = True
self.updateStatusBar(_translate(
"MainWindow", "Shutting down PyBitmessage... %1%").arg(0))
"MainWindow", "Shutting down PyBitmessage... {0}%").format(0))
if waitForConnection:
self.updateStatusBar(_translate(
@ -2824,8 +2850,8 @@ class MyForm(settingsmixin.SMainWindow):
maxWorkerQueue = curWorkerQueue
if curWorkerQueue > 0:
self.updateStatusBar(_translate(
"MainWindow", "Waiting for PoW to finish... %1%"
).arg(50 * (maxWorkerQueue - curWorkerQueue) /
"MainWindow", "Waiting for PoW to finish... {0}%"
).format(50 * (maxWorkerQueue - curWorkerQueue) /
maxWorkerQueue))
time.sleep(0.5)
QtCore.QCoreApplication.processEvents(
@ -2833,7 +2859,7 @@ class MyForm(settingsmixin.SMainWindow):
)
self.updateStatusBar(_translate(
"MainWindow", "Shutting down Pybitmessage... %1%").arg(50))
"MainWindow", "Shutting down Pybitmessage... {0}%").format(50))
QtCore.QCoreApplication.processEvents(
QtCore.QEventLoop.AllEvents, 1000
@ -2847,14 +2873,15 @@ class MyForm(settingsmixin.SMainWindow):
# check if upload (of objects created locally) pending
self.updateStatusBar(_translate(
"MainWindow", "Waiting for objects to be sent... %1%").arg(50))
"MainWindow", "Waiting for objects to be sent... {0}%"
).format(50))
maxPendingUpload = max(1, pendingUpload())
while pendingUpload() > 1:
self.updateStatusBar(_translate(
"MainWindow",
"Waiting for objects to be sent... %1%"
).arg(int(50 + 20 * (pendingUpload() / maxPendingUpload))))
"Waiting for objects to be sent... {0}%"
).format(int(50 + 20 * pendingUpload() / maxPendingUpload)))
time.sleep(0.5)
QtCore.QCoreApplication.processEvents(
QtCore.QEventLoop.AllEvents, 1000
@ -2863,18 +2890,15 @@ class MyForm(settingsmixin.SMainWindow):
QtCore.QCoreApplication.processEvents(
QtCore.QEventLoop.AllEvents, 1000
)
QtCore.QCoreApplication.processEvents(
QtCore.QEventLoop.AllEvents, 1000
)
# save state and geometry self and all widgets
self.updateStatusBar(_translate(
"MainWindow", "Saving settings... %1%").arg(70))
"MainWindow", "Saving settings... {0}%").format(70))
QtCore.QCoreApplication.processEvents(
QtCore.QEventLoop.AllEvents, 1000
)
self.saveSettings()
for attr, obj in self.ui.__dict__.iteritems():
for attr, obj in six.iteritems(self.ui.__dict__):
if hasattr(obj, "__class__") \
and isinstance(obj, settingsmixin.SettingsMixin):
saveMethod = getattr(obj, "saveSettings", None)
@ -2882,18 +2906,18 @@ class MyForm(settingsmixin.SMainWindow):
obj.saveSettings()
self.updateStatusBar(_translate(
"MainWindow", "Shutting down core... %1%").arg(80))
"MainWindow", "Shutting down core... {0}%").format(80))
QtCore.QCoreApplication.processEvents(
QtCore.QEventLoop.AllEvents, 1000
)
shutdown.doCleanShutdown()
self.updateStatusBar(_translate(
"MainWindow", "Stopping notifications... %1%").arg(90))
"MainWindow", "Stopping notifications... {0}%").format(90))
self.tray.hide()
self.updateStatusBar(_translate(
"MainWindow", "Shutdown imminent... %1%").arg(100))
"MainWindow", "Shutdown imminent... {0}%").format(100))
logger.info("Shutdown complete")
self.close()
@ -2919,26 +2943,39 @@ class MyForm(settingsmixin.SMainWindow):
if not msgid:
return
queryreturn = sqlQuery(
'''select message from inbox where msgid=?''', msgid)
if queryreturn != []:
for row in queryreturn:
messageText, = row
'SELECT message FROM inbox WHERE msgid=?', sqlite3.Binary(msgid))
if len(queryreturn) < 1:
queryreturn = sqlQuery(
'SELECT message FROM inbox WHERE msgid=CAST(? AS TEXT)', msgid)
try:
lines_raw = queryreturn[-1][0].split('\n')
lines = []
for line in lines_raw:
lines.append(line.decode("utf-8", "replace"))
except IndexError:
lines = ''
lines = messageText.split('\n')
totalLines = len(lines)
for i in xrange(totalLines):
if 'Message ostensibly from ' in lines[i]:
lines[i] = '<p style="font-size: 12px; color: grey;">%s</span></p>' % (
lines[i])
elif lines[i] == '------------------------------------------------------':
lines[i] = (
'<p style="font-size: 12px; color: grey;">%s</span></p>' %
lines[i]
)
elif (
lines[i]
== '------------------------------------------------------'
):
lines[i] = '<hr>'
elif lines[i] == '' and (i+1) < totalLines and \
lines[i+1] != '------------------------------------------------------':
elif (
lines[i] == '' and (i + 1) < totalLines and lines[i + 1]
!= '------------------------------------------------------'
):
lines[i] = '<br><br>'
content = ' '.join(lines) # To keep the whitespace between lines
content = shared.fixPotentiallyInvalidUTF8Data(content)
content = unicode(content, 'utf-8)')
textEdit.setHtml(QtCore.QString(content))
content = unic(ustr(content))
textEdit.setHtml(content)
def on_action_InboxMarkUnread(self):
tableWidget = self.getCurrentMessagelist()
@ -2949,8 +2986,8 @@ class MyForm(settingsmixin.SMainWindow):
# modified = 0
for row in tableWidget.selectedIndexes():
currentRow = row.row()
msgid = tableWidget.item(currentRow, 3).data()
msgids.add(msgid)
msgid = as_msgid(tableWidget.item(currentRow, 3).data())
msgids.add(sqlite3.Binary(msgid))
# if not tableWidget.item(currentRow, 0).unread:
# modified += 1
self.updateUnreadStatus(tableWidget, currentRow, msgid, False)
@ -2958,24 +2995,25 @@ class MyForm(settingsmixin.SMainWindow):
# for 1081
idCount = len(msgids)
# rowcount =
total_row_count = sqlExecuteChunked(
'''UPDATE inbox SET read=0 WHERE msgid IN ({0}) AND read=1''',
False, idCount, *msgids
)
if total_row_count < 1:
sqlExecuteChunked(
'''UPDATE inbox SET read=0 WHERE msgid IN ({0}) AND read=1''',
idCount, *msgids
True, idCount, *msgids
)
self.propagateUnreadCount()
# 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.
# tableWidget.clearSelection() manages to mark the message
# as read again.
# Format predefined text on message reply.
def quoted_text(self, message):
if not config.safeGetBoolean('bitmessagesettings', 'replybelow'):
return '\n\n------------------------------------------------------\n' + message
return (
'\n\n------------------------------------------------------\n' +
message
)
quoteWrapper = textwrap.TextWrapper(
replace_whitespace=False, initial_indent='> ',
@ -3005,7 +3043,7 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.comboBoxSendFrom, self.ui.comboBoxSendFromBroadcast
):
for i in range(box.count()):
if str(box.itemData(i).toPyObject()) == address:
if ustr(box.itemData(i)) == ustr(address):
box.setCurrentIndex(i)
break
else:
@ -3039,17 +3077,23 @@ class MyForm(settingsmixin.SMainWindow):
acct = accountClass(toAddressAtCurrentInboxRow)
fromAddressAtCurrentInboxRow = tableWidget.item(
currentInboxRow, column_from).address
msgid = tableWidget.item(currentInboxRow, 3).data()
msgid = as_msgid(tableWidget.item(currentInboxRow, 3).data())
queryreturn = sqlQuery(
"SELECT message FROM inbox WHERE msgid=?", msgid
) or sqlQuery("SELECT message FROM sent WHERE ackdata=?", msgid)
"SELECT message FROM inbox WHERE msgid=?", sqlite3.Binary(msgid)
) or sqlQuery("SELECT message FROM sent WHERE ackdata=?", sqlite3.Binary(msgid))
if len(queryreturn) < 1:
queryreturn = sqlQuery(
"SELECT message FROM inbox WHERE msgid=CAST(? AS TEXT)", msgid
) or sqlQuery("SELECT message FROM sent WHERE ackdata=CAST(? AS TEXT)", msgid)
if queryreturn != []:
for row in queryreturn:
messageAtCurrentInboxRow, = row
messageAtCurrentInboxRow = messageAtCurrentInboxRow.decode("utf-8", "replace")
acct.parseMessage(
toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow,
tableWidget.item(currentInboxRow, 2).subject,
messageAtCurrentInboxRow)
messageAtCurrentInboxRow
)
widget = {
'subject': self.ui.lineEditSubject,
'from': self.ui.comboBoxSendFrom,
@ -3062,23 +3106,26 @@ class MyForm(settingsmixin.SMainWindow):
)
# toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
elif not config.has_section(toAddressAtCurrentInboxRow):
QtGui.QMessageBox.information(
self, _translate("MainWindow", "Address is gone"),
QtWidgets.QMessageBox.information(
self,
_translate("MainWindow", "Address is gone"),
_translate(
"MainWindow",
"Bitmessage cannot find your address %1. Perhaps you"
"Bitmessage cannot find your address {0}. Perhaps you"
" removed it?"
).arg(toAddressAtCurrentInboxRow), QtGui.QMessageBox.Ok)
).format(toAddressAtCurrentInboxRow),
QtWidgets.QMessageBox.Ok)
elif not config.getboolean(
toAddressAtCurrentInboxRow, 'enabled'):
QtGui.QMessageBox.information(
self, _translate("MainWindow", "Address disabled"),
QtWidgets.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."
), QtGui.QMessageBox.Ok)
" is disabled. You\'ll have to enable it on the \'Your"
" Identities\' tab before using it."
), QtWidgets.QMessageBox.Ok)
else:
self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(toAddressAtCurrentInboxRow)
broadcast_tab_index = self.ui.tabWidgetSend.indexOf(
@ -3096,7 +3143,7 @@ class MyForm(settingsmixin.SMainWindow):
tableWidget.item(currentInboxRow, column_from).label or (
isinstance(acct, GatewayAccount) and
fromAddressAtCurrentInboxRow == acct.relayAddress):
self.ui.lineEditTo.setText(str(acct.fromAddress))
self.ui.lineEditTo.setText(ustr(acct.fromAddress))
else:
self.ui.lineEditTo.setText(
tableWidget.item(currentInboxRow, column_from).accountString()
@ -3111,7 +3158,7 @@ class MyForm(settingsmixin.SMainWindow):
' reply to the chan address.')
if toAddressAtCurrentInboxRow == \
tableWidget.item(currentInboxRow, column_to).label:
self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow))
self.ui.lineEditTo.setText(ustr(toAddressAtCurrentInboxRow))
else:
self.ui.lineEditTo.setText(
tableWidget.item(currentInboxRow, column_to).accountString()
@ -3120,7 +3167,7 @@ class MyForm(settingsmixin.SMainWindow):
self.setSendFromComboBox(toAddressAtCurrentInboxRow)
quotedText = self.quoted_text(
unicode(messageAtCurrentInboxRow, 'utf-8', 'replace'))
unic(ustr(messageAtCurrentInboxRow)))
widget['message'].setPlainText(quotedText)
if acct.subject[0:3] in ('Re:', 'RE:'):
widget['subject'].setText(
@ -3156,14 +3203,15 @@ class MyForm(settingsmixin.SMainWindow):
recipientAddress = tableWidget.item(
currentInboxRow, 0).data(QtCore.Qt.UserRole)
# Let's make sure that it isn't already in the address book
queryreturn = sqlQuery('''select * from blacklist where address=?''',
addressAtCurrentInboxRow)
queryreturn = sqlQuery(
'SELECT * FROM blacklist WHERE address=?',
dbstr(addressAtCurrentInboxRow))
if queryreturn == []:
label = "\"" + tableWidget.item(currentInboxRow, 2).subject + "\" in " + config.get(
recipientAddress, "label")
sqlExecute('''INSERT INTO blacklist VALUES (?,?, ?)''',
label,
addressAtCurrentInboxRow, True)
dbstr(label),
dbstr(addressAtCurrentInboxRow), True)
self.ui.blackwhitelist.rerenderBlackWhiteList()
self.updateStatusBar(_translate(
"MainWindow",
@ -3188,15 +3236,15 @@ class MyForm(settingsmixin.SMainWindow):
messageLists = (messageLists,)
for messageList in messageLists:
if row is not None:
inventoryHash = messageList.item(row, 3).data()
inventoryHash = as_msgid(messageList.item(row, 3).data())
messageList.removeRow(row)
elif inventoryHash is not None:
for i in range(messageList.rowCount() - 1, -1, -1):
if messageList.item(i, 3).data() == inventoryHash:
if as_msgid(messageList.item(i, 3).data()) == inventoryHash:
messageList.removeRow(i)
elif ackData is not None:
for i in range(messageList.rowCount() - 1, -1, -1):
if messageList.item(i, 3).data() == ackData:
if as_msgid(messageList.item(i, 3).data()) == ackData:
messageList.removeRow(i)
# Send item on the Inbox tab to trash
@ -3206,8 +3254,8 @@ class MyForm(settingsmixin.SMainWindow):
return
currentRow = 0
folder = self.getCurrentFolder()
shifted = QtGui.QApplication.queryKeyboardModifiers() \
& QtCore.Qt.ShiftModifier
shifted = (QtWidgets.QApplication.queryKeyboardModifiers() &
QtCore.Qt.ShiftModifier)
tableWidget.setUpdatesEnabled(False)
inventoryHashesToTrash = set()
# ranges in reversed order
@ -3216,16 +3264,21 @@ class MyForm(settingsmixin.SMainWindow):
)[::-1]:
for i in range(r.bottomRow() - r.topRow() + 1):
inventoryHashesToTrash.add(
tableWidget.item(r.topRow() + i, 3).data())
sqlite3.Binary(as_msgid(tableWidget.item(r.topRow() + i, 3).data())))
currentRow = r.topRow()
self.getCurrentMessageTextedit().setText("")
tableWidget.model().removeRows(
r.topRow(), r.bottomRow() - r.topRow() + 1)
idCount = len(inventoryHashesToTrash)
total_row_count = sqlExecuteChunked(
("DELETE FROM inbox" if folder == "trash" or shifted else
"UPDATE inbox SET folder='trash', read=1") +
" WHERE msgid IN ({0})", False, idCount, *inventoryHashesToTrash)
if total_row_count < 1:
sqlExecuteChunked(
("DELETE FROM inbox" if folder == "trash" or shifted else
"UPDATE inbox SET folder='trash', read=1") +
" WHERE msgid IN ({0})", idCount, *inventoryHashesToTrash)
" WHERE msgid IN ({0})", True, idCount, *inventoryHashesToTrash)
tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1)
tableWidget.setUpdatesEnabled(True)
self.propagateUnreadCount(folder)
@ -3244,16 +3297,20 @@ class MyForm(settingsmixin.SMainWindow):
)[::-1]:
for i in range(r.bottomRow() - r.topRow() + 1):
inventoryHashesToTrash.add(
tableWidget.item(r.topRow() + i, 3).data())
sqlite3.Binary(as_msgid(tableWidget.item(r.topRow() + i, 3).data())))
currentRow = r.topRow()
self.getCurrentMessageTextedit().setText("")
tableWidget.model().removeRows(
r.topRow(), r.bottomRow() - r.topRow() + 1)
tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1)
idCount = len(inventoryHashesToTrash)
total_row_count = sqlExecuteChunked(
"UPDATE inbox SET folder='inbox' WHERE msgid IN({0})",
False, idCount, *inventoryHashesToTrash)
if total_row_count < 1:
sqlExecuteChunked(
"UPDATE inbox SET folder='inbox' WHERE msgid IN({0})",
idCount, *inventoryHashesToTrash)
True, idCount, *inventoryHashesToTrash)
tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1)
tableWidget.setUpdatesEnabled(True)
self.propagateUnreadCount()
@ -3265,30 +3322,33 @@ class MyForm(settingsmixin.SMainWindow):
return
currentInboxRow = tableWidget.currentRow()
try:
subjectAtCurrentInboxRow = str(tableWidget.item(
subjectAtCurrentInboxRow = ustr(tableWidget.item(
currentInboxRow, 2).data(QtCore.Qt.UserRole))
except:
subjectAtCurrentInboxRow = ''
# Retrieve the message data out of the SQL database
msgid = tableWidget.item(currentInboxRow, 3).data()
msgid = as_msgid(tableWidget.item(currentInboxRow, 3).data())
queryreturn = sqlQuery(
'''select message from inbox where msgid=?''', msgid)
'SELECT message FROM inbox WHERE msgid=?', sqlite3.Binary(msgid))
if len(queryreturn) < 1:
queryreturn = sqlQuery(
'SELECT message FROM inbox WHERE msgid=CAST(? AS TEXT)', msgid)
if queryreturn != []:
for row in queryreturn:
message, = row
message = message.decode("utf-8", "replace")
defaultFilename = "".join(x for x in subjectAtCurrentInboxRow if x.isalnum()) + '.txt'
filename = QtGui.QFileDialog.getSaveFileName(
self,
_translate("MainWindow","Save As..."),
defaultFilename,
"Text files (*.txt);;All files (*.*)")
if filename == '':
defaultFilename = "".join(
x for x in subjectAtCurrentInboxRow if x.isalnum()) + '.txt'
filename = QtWidgets.QFileDialog.getSaveFileName(
self, _translate("MainWindow", "Save As..."), defaultFilename,
"Text files (*.txt);;All files (*.*)")[0]
if not filename:
return
try:
f = open(filename, 'w')
f.write(message)
f.write(message.encode("utf-8", "replace"))
f.close()
except Exception:
logger.exception('Message not saved', exc_info=True)
@ -3296,18 +3356,26 @@ class MyForm(settingsmixin.SMainWindow):
# Send item on the Sent tab to trash
def on_action_SentTrash(self):
currentRow = 0
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
folder = self.getCurrentFolder()
shifted = QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier
shifted = (QtWidgets.QApplication.queryKeyboardModifiers() &
QtCore.Qt.ShiftModifier)
while tableWidget.selectedIndexes() != []:
currentRow = tableWidget.selectedIndexes()[0].row()
ackdataToTrash = tableWidget.item(currentRow, 3).data()
ackdataToTrash = as_msgid(tableWidget.item(currentRow, 3).data())
rowcount = sqlExecute(
"DELETE FROM sent" if folder == "trash" or shifted else
"UPDATE sent SET folder='trash'"
" WHERE ackdata = ?", sqlite3.Binary(ackdataToTrash)
)
if rowcount < 1:
sqlExecute(
"DELETE FROM sent" if folder == "trash" or shifted else
"UPDATE sent SET folder='trash'"
" WHERE ackdata = ?", ackdataToTrash
" WHERE ackdata = CAST(? AS TEXT)", ackdataToTrash
)
self.getCurrentMessageTextedit().setPlainText("")
tableWidget.removeRow(currentRow)
@ -3322,22 +3390,29 @@ class MyForm(settingsmixin.SMainWindow):
addressAtCurrentRow = self.ui.tableWidgetInbox.item(
currentRow, 0).data(QtCore.Qt.UserRole)
toRipe = decodeAddress(addressAtCurrentRow)[3]
sqlExecute(
rowcount = sqlExecute(
'''UPDATE sent SET status='forcepow' WHERE toripe=? AND status='toodifficult' and folder='sent' ''',
sqlite3.Binary(toRipe))
if rowcount < 1:
sqlExecute(
'''UPDATE sent SET status='forcepow' WHERE toripe=CAST(? AS TEXT) AND status='toodifficult' and folder='sent' ''',
toRipe)
queryreturn = sqlQuery('''select ackdata FROM sent WHERE status='forcepow' ''')
for row in queryreturn:
ackdata, = row
queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (
ackdata, 'Overriding maximum-difficulty setting. Work queued.')))
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata',
(ackdata, 'Overriding maximum-difficulty setting.'
' Work queued.')
))
queues.workerQueue.put(('sendmessage', ''))
def on_action_SentClipboard(self):
currentRow = self.ui.tableWidgetInbox.currentRow()
addressAtCurrentRow = self.ui.tableWidgetInbox.item(
currentRow, 0).data(QtCore.Qt.UserRole)
clipboard = QtGui.QApplication.clipboard()
clipboard.setText(str(addressAtCurrentRow))
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(ustr(addressAtCurrentRow))
# Group of functions for the Address Book dialog box
def on_action_AddressBookNew(self):
@ -3349,7 +3424,7 @@ class MyForm(settingsmixin.SMainWindow):
0].row()
item = self.ui.tableWidgetAddressBook.item(currentRow, 0)
sqlExecute(
'DELETE FROM addressbook WHERE address=?', item.address)
'DELETE FROM addressbook WHERE address=?', dbstr(item.address))
self.ui.tableWidgetAddressBook.removeRow(currentRow)
self.rerenderMessagelistFromLabels()
self.rerenderMessagelistToLabels()
@ -3361,7 +3436,7 @@ class MyForm(settingsmixin.SMainWindow):
addresses_string = item.address
else:
addresses_string += ', ' + item.address
clipboard = QtGui.QApplication.clipboard()
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(addresses_string)
def on_action_AddressBookSend(self):
@ -3371,8 +3446,7 @@ class MyForm(settingsmixin.SMainWindow):
return self.updateStatusBar(_translate(
"MainWindow", "No addresses selected."))
addresses_string = unicode(
self.ui.lineEditTo.text().toUtf8(), 'utf-8')
addresses_string = unic(ustr(self.ui.lineEditTo.text()))
for item in selected_items:
address_string = item.accountString()
if not addresses_string:
@ -3403,7 +3477,7 @@ class MyForm(settingsmixin.SMainWindow):
)
def on_context_menuAddressBook(self, point):
self.popMenuAddressBook = QtGui.QMenu(self)
self.popMenuAddressBook = QtWidgets.QMenu(self)
self.popMenuAddressBook.addAction(self.actionAddressBookSend)
self.popMenuAddressBook.addAction(self.actionAddressBookClipboard)
self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe)
@ -3433,7 +3507,7 @@ class MyForm(settingsmixin.SMainWindow):
self.click_pushButtonAddSubscription()
def on_action_SubscriptionsDelete(self):
if QtGui.QMessageBox.question(
if QtWidgets.QMessageBox.question(
self, "Delete subscription?",
_translate(
"MainWindow",
@ -3444,12 +3518,12 @@ class MyForm(settingsmixin.SMainWindow):
" messages, but you can still view messages you"
" already received.\n\nAre you sure you want to"
" delete the subscription?"
), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
) != QtGui.QMessageBox.Yes:
), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
) != QtWidgets.QMessageBox.Yes:
return
address = self.getCurrentAccount()
sqlExecute('''DELETE FROM subscriptions WHERE address=?''',
address)
dbstr(address))
self.rerenderTabTreeSubscriptions()
self.rerenderMessagelistFromLabels()
self.rerenderAddressBook()
@ -3457,14 +3531,14 @@ class MyForm(settingsmixin.SMainWindow):
def on_action_SubscriptionsClipboard(self):
address = self.getCurrentAccount()
clipboard = QtGui.QApplication.clipboard()
clipboard.setText(str(address))
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(ustr(address))
def on_action_SubscriptionsEnable(self):
address = self.getCurrentAccount()
sqlExecute(
'''update subscriptions set enabled=1 WHERE address=?''',
address)
dbstr(address))
account = self.getCurrentItem()
account.setEnabled(True)
self.rerenderAddressBook()
@ -3474,7 +3548,7 @@ class MyForm(settingsmixin.SMainWindow):
address = self.getCurrentAccount()
sqlExecute(
'''update subscriptions set enabled=0 WHERE address=?''',
address)
dbstr(address))
account = self.getCurrentItem()
account.setEnabled(False)
self.rerenderAddressBook()
@ -3482,7 +3556,7 @@ class MyForm(settingsmixin.SMainWindow):
def on_context_menuSubscriptions(self, point):
currentItem = self.getCurrentItem()
self.popMenuSubscriptions = QtGui.QMenu(self)
self.popMenuSubscriptions = QtWidgets.QMenu(self)
if isinstance(currentItem, Ui_AddressWidget):
self.popMenuSubscriptions.addAction(self.actionsubscriptionsNew)
self.popMenuSubscriptions.addAction(self.actionsubscriptionsDelete)
@ -3522,8 +3596,6 @@ class MyForm(settingsmixin.SMainWindow):
return self.ui.tableWidgetInboxSubscriptions
elif widget == self.ui.treeWidgetChans:
return self.ui.tableWidgetInboxChans
else:
return None
def getCurrentTreeWidget(self):
currentIndex = self.ui.tabWidget.currentIndex()
@ -3576,7 +3648,7 @@ class MyForm(settingsmixin.SMainWindow):
if messagelist:
currentRow = messagelist.currentRow()
if currentRow >= 0:
return messagelist.item(currentRow, 3).data()
return as_msgid(messagelist.item(currentRow, 3).data())
def getCurrentMessageTextedit(self):
currentIndex = self.ui.tabWidget.currentIndex()
@ -3610,9 +3682,9 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.inboxSearchLineEditChans,
)
if currentIndex >= 0 and currentIndex < len(messagelistList):
return (
return ustr(
messagelistList[currentIndex] if retObj
else messagelistList[currentIndex].text().toUtf8().data())
else ustr(messagelistList[currentIndex].text()))
def getCurrentSearchOption(self, currentIndex=None):
if currentIndex is None:
@ -3668,7 +3740,7 @@ class MyForm(settingsmixin.SMainWindow):
if account.type == AccountMixin.NORMAL:
return # maybe in the future
elif account.type == AccountMixin.CHAN:
if QtGui.QMessageBox.question(
if QtWidgets.QMessageBox.question(
self, "Delete channel?",
_translate(
"MainWindow",
@ -3679,9 +3751,9 @@ class MyForm(settingsmixin.SMainWindow):
" messages, but you can still view messages you"
" already received.\n\nAre you sure you want to"
" delete the channel?"
), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
) == QtGui.QMessageBox.Yes:
config.remove_section(str(account.address))
), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
) == QtWidgets.QMessageBox.Yes:
config.remove_section(ustr(account.address))
else:
return
else:
@ -3714,15 +3786,15 @@ class MyForm(settingsmixin.SMainWindow):
account.setEnabled(False)
def disableIdentity(self, address):
config.set(str(address), 'enabled', 'false')
config.set(ustr(address), 'enabled', 'false')
config.save()
shared.reloadMyAddressHashes()
self.rerenderAddressBook()
def on_action_Clipboard(self):
address = self.getCurrentAccount()
clipboard = QtGui.QApplication.clipboard()
clipboard.setText(str(address))
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(ustr(address))
def on_action_ClipboardMessagelist(self):
tableWidget = self.getCurrentMessagelist()
@ -3739,14 +3811,14 @@ class MyForm(settingsmixin.SMainWindow):
myAddress = tableWidget.item(currentRow, 0).data(QtCore.Qt.UserRole)
otherAddress = tableWidget.item(currentRow, 1).data(QtCore.Qt.UserRole)
account = accountClass(myAddress)
if isinstance(account, GatewayAccount) and otherAddress == account.relayAddress and (
(currentColumn in [0, 2] and self.getCurrentFolder() == "sent") or
(currentColumn in [1, 2] and self.getCurrentFolder() != "sent")):
text = str(tableWidget.item(currentRow, currentColumn).label)
if isinstance(account, GatewayAccount) \
and otherAddress == account.relayAddress and (
(currentColumn in (0, 2) and currentFolder == "sent") or
(currentColumn in (1, 2) and currentFolder != "sent")):
text = ustr(tableWidget.item(currentRow, currentColumn).label)
else:
text = tableWidget.item(currentRow, currentColumn).data(QtCore.Qt.UserRole)
clipboard = QtGui.QApplication.clipboard()
text = ustr(tableWidget.item(currentRow, currentColumn).data(QtCore.Qt.UserRole))
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(text)
# set avatar functions
@ -3759,8 +3831,8 @@ class MyForm(settingsmixin.SMainWindow):
def on_action_SetAvatar(self, thisTableWidget):
currentRow = thisTableWidget.currentRow()
addressAtCurrentRow = thisTableWidget.item(
currentRow, 1).text()
addressAtCurrentRow = ustr(thisTableWidget.item(
currentRow, 1).text())
setToIdenticon = not self.setAvatar(addressAtCurrentRow)
if setToIdenticon:
thisTableWidget.item(
@ -3770,11 +3842,8 @@ class MyForm(settingsmixin.SMainWindow):
def setAvatar(self, addressAtCurrentRow):
if not os.path.exists(state.appdata + 'avatars/'):
os.makedirs(state.appdata + 'avatars/')
hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow)).hexdigest()
extensions = [
'PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM',
'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA']
hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow).encode("utf-8", "replace")).hexdigest()
# http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats
names = {
'BMP': 'Windows Bitmap',
'GIF': 'Graphic Interchange Format',
@ -3789,11 +3858,12 @@ class MyForm(settingsmixin.SMainWindow):
'XBM': 'X11 Bitmap',
'XPM': 'X11 Pixmap',
'SVG': 'Scalable Vector Graphics',
'TGA': 'Targa Image Format'}
'TGA': 'Targa Image Format'
}
filters = []
all_images_filter = []
current_files = []
for ext in extensions:
for ext in names:
filters += [names[ext] + ' (*.' + ext.lower() + ')']
all_images_filter += ['*.' + ext.lower()]
upper = state.appdata + 'avatars/' + hash + '.' + ext.upper()
@ -3804,10 +3874,9 @@ class MyForm(settingsmixin.SMainWindow):
current_files += [upper]
filters[0:0] = ['Image files (' + ' '.join(all_images_filter) + ')']
filters[1:1] = ['All files (*.*)']
sourcefile = QtGui.QFileDialog.getOpenFileName(
sourcefile = QtWidgets.QFileDialog.getOpenFileName(
self, _translate("MainWindow", "Set avatar..."),
filter=';;'.join(filters)
)
filter=';;'.join(filters))[0]
# determine the correct filename (note that avatars don't use the suffix)
destination = state.appdata + 'avatars/' + hash + '.' + sourcefile.split('.')[-1]
exists = QtCore.QFile.exists(destination)
@ -3816,11 +3885,11 @@ class MyForm(settingsmixin.SMainWindow):
if exists | (len(current_files) > 0):
displayMsg = _translate(
"MainWindow", "Do you really want to remove this avatar?")
overwrite = QtGui.QMessageBox.question(
overwrite = QtWidgets.QMessageBox.question(
self, 'Message', displayMsg,
QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
else:
overwrite = QtGui.QMessageBox.No
overwrite = QtWidgets.QMessageBox.No
else:
# ask whether to overwrite old avatar
if exists | (len(current_files) > 0):
@ -3828,15 +3897,15 @@ class MyForm(settingsmixin.SMainWindow):
"MainWindow",
"You have already set an avatar for this address."
" Do you really want to overwrite it?")
overwrite = QtGui.QMessageBox.question(
overwrite = QtWidgets.QMessageBox.question(
self, 'Message', displayMsg,
QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
else:
overwrite = QtGui.QMessageBox.No
overwrite = QtWidgets.QMessageBox.No
# copy the image file to the appdata folder
if (not exists) | (overwrite == QtGui.QMessageBox.Yes):
if overwrite == QtGui.QMessageBox.Yes:
if (not exists) | (overwrite == QtWidgets.QMessageBox.Yes):
if overwrite == QtWidgets.QMessageBox.Yes:
for file in current_files:
QtCore.QFile.remove(file)
QtCore.QFile.remove(destination)
@ -3861,23 +3930,23 @@ class MyForm(settingsmixin.SMainWindow):
def on_action_AddressBookSetSound(self):
widget = self.ui.tableWidgetAddressBook
self.setAddressSound(widget.item(widget.currentRow(), 0).text())
self.setAddressSound(ustr(widget.item(widget.currentRow(), 0).text()))
def setAddressSound(self, addr):
filters = [unicode(_translate(
filters = [unic(_translate(
"MainWindow", "Sound files (%s)" %
' '.join(['*%s%s' % (os.extsep, ext) for ext in sound.extensions])
))]
sourcefile = unicode(QtGui.QFileDialog.getOpenFileName(
sourcefile = unic(ustr(QtWidgets.QFileDialog.getOpenFileName(
self, _translate("MainWindow", "Set notification sound..."),
filter=';;'.join(filters)
))
)))[0]
if not sourcefile:
return
destdir = os.path.join(state.appdata, 'sounds')
destfile = unicode(addr) + os.path.splitext(sourcefile)[-1]
destfile = unic(ustr(addr) + os.path.splitext(sourcefile)[-1])
destination = os.path.join(destdir, destfile)
if sourcefile == destination:
@ -3886,15 +3955,15 @@ class MyForm(settingsmixin.SMainWindow):
pattern = destfile.lower()
for item in os.listdir(destdir):
if item.lower() == pattern:
overwrite = QtGui.QMessageBox.question(
overwrite = QtWidgets.QMessageBox.question(
self, _translate("MainWindow", "Message"),
_translate(
"MainWindow",
"You have already set a notification sound"
" for this address book entry."
" Do you really want to overwrite it?"),
QtGui.QMessageBox.Yes, QtGui.QMessageBox.No
) == QtGui.QMessageBox.Yes
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No
) == QtWidgets.QMessageBox.Yes
if overwrite:
QtCore.QFile.remove(os.path.join(destdir, item))
break
@ -3905,18 +3974,23 @@ class MyForm(settingsmixin.SMainWindow):
def on_context_menuYourIdentities(self, point):
currentItem = self.getCurrentItem()
self.popMenuYourIdentities = QtGui.QMenu(self)
self.popMenuYourIdentities = QtWidgets.QMenu(self)
if isinstance(currentItem, Ui_AddressWidget):
self.popMenuYourIdentities.addAction(self.actionNewYourIdentities)
self.popMenuYourIdentities.addSeparator()
self.popMenuYourIdentities.addAction(self.actionClipboardYourIdentities)
self.popMenuYourIdentities.addAction(
self.actionClipboardYourIdentities)
self.popMenuYourIdentities.addSeparator()
if currentItem.isEnabled:
self.popMenuYourIdentities.addAction(self.actionDisableYourIdentities)
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.actionEnableYourIdentities)
self.popMenuYourIdentities.addAction(
self.actionSetAvatarYourIdentities)
self.popMenuYourIdentities.addAction(
self.actionSpecialAddressBehaviorYourIdentities)
self.popMenuYourIdentities.addAction(self.actionEmailGateway)
self.popMenuYourIdentities.addSeparator()
if currentItem.type != AccountMixin.ALL:
@ -3935,7 +4009,7 @@ class MyForm(settingsmixin.SMainWindow):
# TODO make one popMenu
def on_context_menuChan(self, point):
currentItem = self.getCurrentItem()
self.popMenu = QtGui.QMenu(self)
self.popMenu = QtWidgets.QMenu(self)
if isinstance(currentItem, Ui_AddressWidget):
self.popMenu.addAction(self.actionNew)
self.popMenu.addAction(self.actionDelete)
@ -3971,7 +4045,7 @@ class MyForm(settingsmixin.SMainWindow):
self.on_context_menuSent(point)
return
self.popMenuInbox = QtGui.QMenu(self)
self.popMenuInbox = QtWidgets.QMenu(self)
self.popMenuInbox.addAction(self.actionForceHtml)
self.popMenuInbox.addAction(self.actionMarkUnread)
self.popMenuInbox.addSeparator()
@ -4006,7 +4080,7 @@ class MyForm(settingsmixin.SMainWindow):
def on_context_menuSent(self, point):
currentRow = self.ui.tableWidgetInbox.currentRow()
self.popMenuSent = QtGui.QMenu(self)
self.popMenuSent = QtWidgets.QMenu(self)
self.popMenuSent.addAction(self.actionSentClipboard)
self._contact_selected = self.ui.tableWidgetInbox.item(currentRow, 0)
# preloaded gui.menu plugins with prefix 'address'
@ -4019,10 +4093,13 @@ class MyForm(settingsmixin.SMainWindow):
# Check to see if this item is toodifficult and display an additional
# menu option (Force Send) if it is.
if currentRow >= 0:
ackData = self.ui.tableWidgetInbox.item(currentRow, 3).data()
queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData)
ackData = as_msgid(self.ui.tableWidgetInbox.item(currentRow, 3).data())
queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', sqlite3.Binary(ackData))
if len(queryreturn) < 1:
queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=CAST(? AS TEXT)''', ackData)
for row in queryreturn:
status, = row
status = status.decode("utf-8", "replace")
if status == 'toodifficult':
self.popMenuSent.addAction(self.actionForceSend)
@ -4030,7 +4107,7 @@ class MyForm(settingsmixin.SMainWindow):
def inboxSearchLineEditUpdated(self, text):
# dynamic search for too short text is slow
text = text.toUtf8()
text = ustr(text)
if 0 < len(text) < 3:
return
messagelist = self.getCurrentMessagelist()
@ -4043,9 +4120,9 @@ class MyForm(settingsmixin.SMainWindow):
def inboxSearchLineEditReturnPressed(self):
logger.debug("Search return pressed")
searchLine = self.getCurrentSearchLine()
searchLine = self.getCurrentSearchLine().encode('utf-8')
messagelist = self.getCurrentMessagelist()
if messagelist and len(str(searchLine)) < 3:
if messagelist and len(ustr(searchLine)) < 3:
searchOption = self.getCurrentSearchOption()
account = self.getCurrentAccount()
folder = self.getCurrentFolder()
@ -4087,7 +4164,7 @@ class MyForm(settingsmixin.SMainWindow):
if item.type == AccountMixin.ALL:
return
newLabel = unicode(item.text(0), 'utf-8', 'ignore')
newLabel = unic(ustr(item.text(0)))
oldLabel = item.defaultLabel()
# unchanged, do not do anything either
@ -4106,7 +4183,9 @@ class MyForm(settingsmixin.SMainWindow):
self.rerenderMessagelistFromLabels()
if item.type != AccountMixin.SUBSCRIPTION:
self.rerenderMessagelistToLabels()
if item.type in (AccountMixin.NORMAL, AccountMixin.CHAN, AccountMixin.SUBSCRIPTION):
if item.type in (
AccountMixin.NORMAL, AccountMixin.CHAN, AccountMixin.SUBSCRIPTION
):
self.rerenderAddressBook()
self.recurDepth -= 1
@ -4119,17 +4198,26 @@ class MyForm(settingsmixin.SMainWindow):
folder = self.getCurrentFolder()
if msgid:
queryreturn = sqlQuery(
'''SELECT message FROM %s WHERE %s=?''' % (
'SELECT message FROM %s WHERE %s=?' % (
('sent', 'ackdata') if folder == 'sent'
else ('inbox', 'msgid')
), sqlite3.Binary(msgid)
)
if len(queryreturn) < 1:
queryreturn = sqlQuery(
'SELECT message FROM %s WHERE %s=CAST(? AS TEXT)' % (
('sent', 'ackdata') if folder == 'sent'
else ('inbox', 'msgid')
), msgid
)
try:
message = queryreturn[-1][0]
message = queryreturn[-1][0].decode("utf-8", "replace")
except NameError:
message = ""
message = u''
except IndexError:
# _translate() often returns unicode, no redefinition here!
# pylint: disable=redefined-variable-type
message = _translate(
"MainWindow",
"Error occurred: could not load message from disk."
@ -4141,10 +4229,16 @@ class MyForm(settingsmixin.SMainWindow):
if tableWidget.item(currentRow, 0).unread is True:
self.updateUnreadStatus(tableWidget, currentRow, msgid)
# propagate
if folder != 'sent' and sqlExecute(
'''UPDATE inbox SET read=1 WHERE msgid=? AND read=0''',
rowcount = sqlExecute(
'UPDATE inbox SET read=1 WHERE msgid=? AND read=0',
sqlite3.Binary(msgid)
)
if rowcount < 1:
rowcount = sqlExecute(
'UPDATE inbox SET read=1 WHERE msgid=CAST(? AS TEXT) AND read=0',
msgid
) > 0:
)
if folder != 'sent' and rowcount > 0:
self.propagateUnreadCount()
messageTextedit.setCurrentFont(QtGui.QFont())
@ -4158,8 +4252,9 @@ class MyForm(settingsmixin.SMainWindow):
self.rerenderMessagelistToLabels()
completerList = self.ui.lineEditTo.completer().model().stringList()
for i in range(len(completerList)):
if unicode(completerList[i]).endswith(" <" + item.address + ">"):
completerList[i] = item.label + " <" + item.address + ">"
address_block = " <" + ustr(item.address) + ">"
if unic(ustr(completerList[i])).endswith(unic(address_block)):
completerList[i] = ustr(item.label) + address_block
self.ui.lineEditTo.completer().model().setStringList(completerList)
def tabWidgetCurrentChanged(self, n):
@ -4212,7 +4307,7 @@ class MyForm(settingsmixin.SMainWindow):
def initSettings(self):
self.loadSettings()
for attr, obj in self.ui.__dict__.iteritems():
for attr, obj in six.iteritems(self.ui.__dict__):
if hasattr(obj, "__class__") and \
isinstance(obj, settingsmixin.SettingsMixin):
loadMethod = getattr(obj, "loadSettings", None)
@ -4224,7 +4319,7 @@ app = None
myapp = None
class BitmessageQtApplication(QtGui.QApplication):
class BitmessageQtApplication(QtWidgets.QApplication):
"""
Listener to allow our Qt form to get focus when another instance of the
application is open.
@ -4263,15 +4358,15 @@ class BitmessageQtApplication(QtGui.QApplication):
self.server = None
self.is_running = False
socket = QLocalSocket()
socket = QtNetwork.QLocalSocket()
socket.connectToServer(id)
self.is_running = socket.waitForConnected()
# Cleanup past crashed servers
if not self.is_running:
if socket.error() == QLocalSocket.ConnectionRefusedError:
if socket.error() == QtNetwork.QLocalSocket.ConnectionRefusedError:
socket.disconnectFromServer()
QLocalServer.removeServer(id)
QtNetwork.QLocalServer.removeServer(id)
socket.abort()
@ -4282,16 +4377,12 @@ class BitmessageQtApplication(QtGui.QApplication):
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 = QtNetwork.QLocalServer()
self.server.listen(id)
self.server.newConnection.connect(self.on_new_connection)
self.setStyleSheet("QStatusBar::item { border: 0px solid black }")
def __del__(self):
if self.server:
self.server.close()
def on_new_connection(self):
if myapp:
myapp.appIndicatorShow()

View File

@ -1,20 +1,16 @@
# pylint: disable=too-many-instance-attributes,attribute-defined-outside-init
"""
account.py
==========
Account related functions.
"""
from __future__ import absolute_import
import inspect
import re
import sys
import time
import sqlite3
from PyQt4 import QtGui
from unqstr import ustr, unic
from dbcompat import dbstr
import queues
from addresses import decodeAddress
@ -23,6 +19,7 @@ from helper_ackPayload import genAckPayload
from helper_sql import sqlQuery, sqlExecute
from .foldertree import AccountMixin
from .utils import str_broadcast_subscribers
from tr import _translate
def getSortedSubscriptions(count=False):
@ -34,22 +31,25 @@ def getSortedSubscriptions(count=False):
:retuns: dict keys are addresses, values are dicts containing settings
:rtype: dict, default {}
"""
queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions ORDER BY label COLLATE NOCASE ASC')
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
for label, address, enabled in queryreturn:
label = label.decode("utf-8", "replace")
address = address.decode("utf-8", "replace")
ret[address] = {'inbox': {}}
ret[address]['inbox'].update(label=label, enabled=enabled, count=0)
if count:
queryreturn = sqlQuery('''SELECT fromaddress, folder, count(msgid) as cnt
FROM inbox, subscriptions ON subscriptions.address = inbox.fromaddress
WHERE read = 0 AND toaddress = ?
GROUP BY inbox.fromaddress, folder''', str_broadcast_subscribers)
for row in queryreturn:
address, folder, cnt = row
queryreturn = sqlQuery(
'SELECT fromaddress, folder, count(msgid) AS cnt'
' FROM inbox, subscriptions'
' ON subscriptions.address = inbox.fromaddress WHERE read = 0'
' AND toaddress = ? GROUP BY inbox.fromaddress, folder',
dbstr(str_broadcast_subscribers))
for address, folder, cnt in queryreturn:
address = address.decode("utf-8", "replace")
folder = folder.decode("utf-8", "replace")
if folder not in ret[address]:
ret[address][folder] = {
'label': ret[address]['inbox']['label'],
@ -75,7 +75,8 @@ def accountClass(address):
return subscription
try:
gateway = config.get(address, "gateway")
for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass):
for _, cls in inspect.getmembers(
sys.modules[__name__], inspect.isclass):
if issubclass(cls, GatewayAccount) and cls.gatewayName == gateway:
return cls(address)
# general gateway
@ -86,7 +87,7 @@ def accountClass(address):
return BMAccount(address)
class AccountColor(AccountMixin): # pylint: disable=too-few-public-methods
class AccountColor(AccountMixin):
"""Set the type of account"""
def __init__(self, address, address_type=None):
@ -100,7 +101,9 @@ class AccountColor(AccountMixin): # pylint: disable=too-few-public-methods
elif config.safeGetBoolean(self.address, 'chan'):
self.type = AccountMixin.CHAN
elif sqlQuery(
'''select label from subscriptions where address=?''', self.address):
'SELECT label FROM subscriptions WHERE address=?',
dbstr(self.address)
):
self.type = AccountMixin.SUBSCRIPTION
else:
self.type = AccountMixin.NORMAL
@ -108,12 +111,35 @@ class AccountColor(AccountMixin): # pylint: disable=too-few-public-methods
self.type = address_type
class BMAccount(object):
"""Encapsulate a Bitmessage account"""
class NoAccount(object):
"""Minimal account like object (All accounts)"""
# pylint: disable=too-many-instance-attributes
def __init__(self, address=None):
self.address = address
self.type = AccountMixin.NORMAL
self.toAddress = self.fromAddress = ''
self.subject = self.message = ''
self.fromLabel = self.toLabel = ''
def getLabel(self, address=None):
"""Get a label for this bitmessage account"""
return address or self.address
def parseMessage(self, toAddress, fromAddress, subject, message):
"""Set metadata and address labels on self"""
self.toAddress = ustr(toAddress)
self.fromAddress = ustr(fromAddress)
self.subject = ustr(subject)
self.message = ustr(message)
self.fromLabel = ustr(self.getLabel(fromAddress))
self.toLabel = ustr(self.getLabel(toAddress))
class BMAccount(NoAccount):
"""Encapsulate a Bitmessage account"""
def __init__(self, address=None):
super(BMAccount, self).__init__(address)
if config.has_section(address):
if config.safeGetBoolean(self.address, 'chan'):
self.type = AccountMixin.CHAN
@ -121,55 +147,25 @@ class BMAccount(object):
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:
elif sqlQuery(
'SELECT label FROM subscriptions WHERE address=?', dbstr(self.address)
):
self.type = AccountMixin.SUBSCRIPTION
def getLabel(self, address=None):
"""Get a label for this bitmessage account"""
if address is None:
address = self.address
address = super(BMAccount, self).getLabel(address)
label = config.safeGet(address, 'label', address)
queryreturn = sqlQuery(
'''select label from addressbook where address=?''', address)
if queryreturn != []:
for row in queryreturn:
label, = row
'SELECT label FROM addressbook WHERE address=?', dbstr(address))
if queryreturn:
label = queryreturn[-1][0]
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):
"""Set metadata and address labels on self"""
self.toAddress = toAddress
self.fromAddress = fromAddress
if isinstance(subject, unicode):
self.subject = str(subject)
else:
self.subject = subject
self.message = message
self.fromLabel = self.getLabel(fromAddress)
self.toLabel = self.getLabel(toAddress)
class NoAccount(BMAccount):
"""Override the __init__ method on a BMAccount"""
def __init__(self, address=None): # pylint: disable=super-init-not-called
self.address = address
self.type = AccountMixin.NORMAL
def getLabel(self, address=None):
if address is None:
address = self.address
return address
'SELECT label FROM subscriptions WHERE address=?', dbstr(address))
if queryreturn:
label = queryreturn[-1][0]
return unic(ustr(label))
class SubscriptionAccount(BMAccount):
@ -189,31 +185,27 @@ class GatewayAccount(BMAccount):
ALL_OK = 0
REGISTRATION_DENIED = 1
def __init__(self, address):
super(GatewayAccount, self).__init__(address)
def send(self):
"""Override the send method for gateway accounts"""
# pylint: disable=unused-variable
status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.toAddress)
stealthLevel = config.safeGetInt('bitmessagesettings', 'ackstealthlevel')
"""The send method for gateway accounts"""
streamNumber, ripe = decodeAddress(self.toAddress)[2:]
stealthLevel = config.safeGetInt(
'bitmessagesettings', 'ackstealthlevel')
ackdata = genAckPayload(streamNumber, stealthLevel)
sqlExecute(
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
'',
self.toAddress,
ripe,
self.fromAddress,
self.subject,
self.message,
ackdata,
sqlite3.Binary(b''),
dbstr(self.toAddress),
sqlite3.Binary(ripe),
dbstr(self.fromAddress),
dbstr(self.subject),
dbstr(self.message),
sqlite3.Binary(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',
dbstr('msgqueued'),
0, # retryNumber
'sent', # folder
dbstr('sent'), # folder
2, # encodingtype
# not necessary to have a TTL higher than 2 days
min(config.getint('bitmessagesettings', 'ttl'), 86400 * 2)
@ -270,10 +262,9 @@ class MailchuckAccount(GatewayAccount):
def settings(self):
"""settings specific to a MailchuckAccount"""
self.toAddress = self.registrationAddress
self.subject = "config"
self.message = QtGui.QApplication.translate(
self.message = _translate(
"Mailchuck",
"""# You can use this to configure your email gateway account
# Uncomment the setting you want to use
@ -319,8 +310,9 @@ class MailchuckAccount(GatewayAccount):
def parseMessage(self, toAddress, fromAddress, subject, message):
"""parseMessage specific to a MailchuckAccount"""
super(MailchuckAccount, self).parseMessage(toAddress, fromAddress, subject, message)
super(MailchuckAccount, self).parseMessage(
toAddress, fromAddress, subject, message
)
if fromAddress == self.relayAddress:
matches = self.regExpIncoming.search(subject)
if matches is not None:
@ -341,6 +333,7 @@ class MailchuckAccount(GatewayAccount):
self.toLabel = matches.group(1)
self.toAddress = matches.group(1)
self.feedback = self.ALL_OK
if fromAddress == self.registrationAddress and self.subject == "Registration Request Denied":
if fromAddress == self.registrationAddress \
and self.subject == "Registration Request Denied":
self.feedback = self.REGISTRATION_DENIED
return self.feedback

View File

@ -1,40 +1,40 @@
"""
Dialogs that work with BM address.
"""
# pylint: disable=attribute-defined-outside-init,too-few-public-methods,relative-import
# pylint: disable=too-few-public-methods
# https://github.com/PyCQA/pylint/issues/471
import hashlib
from PyQt4 import QtCore, QtGui
from unqstr import ustr, unic
from qtpy import QtGui, QtWidgets
import queues
import widgets
from bitmessageqt import widgets
import state
from account import AccountMixin, GatewayAccount, MailchuckAccount, accountClass
from .account import (
GatewayAccount, MailchuckAccount, AccountMixin, accountClass
)
from addresses import addBMIfNotPresent, decodeAddress, encodeVarint
from bmconfigparser import config as global_config
from tr import _translate
class AddressCheckMixin(object):
"""Base address validation class for QT UI"""
"""Base address validation class for Qt UI"""
def __init__(self):
def _setup(self):
self.valid = False
QtCore.QObject.connect( # pylint: disable=no-member
self.lineEditAddress,
QtCore.SIGNAL("textChanged(QString)"),
self.addressChanged)
self.lineEditAddress.textChanged.connect(self.addressChanged)
def _onSuccess(self, addressVersion, streamNumber, ripe):
pass
def addressChanged(self, QString):
def addressChanged(self, address):
"""
Address validation callback, performs validation and gives feedback
"""
status, addressVersion, streamNumber, ripe = decodeAddress(
str(QString))
status, addressVersion, streamNumber, ripe = decodeAddress(ustr(address))
self.valid = status == 'success'
if self.valid:
self.labelAddressCheck.setText(
@ -79,19 +79,27 @@ class AddressCheckMixin(object):
))
class AddressDataDialog(QtGui.QDialog, AddressCheckMixin):
"""QDialog with Bitmessage address validation"""
class AddressDataDialog(QtWidgets.QDialog, AddressCheckMixin):
"""
Base class for a dialog getting BM-address data.
Corresponding ui-file should define two fields:
lineEditAddress - for the address
lineEditLabel - for it's label
After address validation the values of that fields are put into
the data field of the dialog.
"""
def __init__(self, parent):
super(AddressDataDialog, self).__init__(parent)
self.parent = parent
self.data = None
def accept(self):
"""Callback for QDIalog accepting value"""
"""Callback for QDialog accepting value"""
if self.valid:
self.data = (
addBMIfNotPresent(str(self.lineEditAddress.text())),
str(self.lineEditLabel.text().toUtf8())
addBMIfNotPresent(ustr(self.lineEditAddress.text())),
ustr(self.lineEditLabel.text())
)
else:
queues.UISignalQueue.put(('updateStatusBar', _translate(
@ -107,12 +115,12 @@ class AddAddressDialog(AddressDataDialog):
def __init__(self, parent=None, address=None):
super(AddAddressDialog, self).__init__(parent)
widgets.load('addaddressdialog.ui', self)
AddressCheckMixin.__init__(self)
self._setup()
if address:
self.lineEditAddress.setText(address)
class NewAddressDialog(QtGui.QDialog):
class NewAddressDialog(QtWidgets.QDialog):
"""QDialog for generating a new address"""
def __init__(self, parent=None):
@ -125,7 +133,7 @@ class NewAddressDialog(QtGui.QDialog):
self.radioButtonExisting.click()
self.comboBoxExisting.addItem(address)
self.groupBoxDeterministic.setHidden(True)
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
self.show()
def accept(self):
@ -142,13 +150,13 @@ class NewAddressDialog(QtGui.QDialog):
self.comboBoxExisting.currentText())[2]
queues.addressGeneratorQueue.put((
'createRandomAddress', 4, streamNumberForAddress,
str(self.newaddresslabel.text().toUtf8()), 1, "",
ustr(self.newaddresslabel.text()), 1, "",
self.checkBoxEighteenByteRipe.isChecked()
))
else:
if self.lineEditPassphrase.text() != \
self.lineEditPassphraseAgain.text():
QtGui.QMessageBox.about(
if ustr(self.lineEditPassphrase.text()) != \
ustr(self.lineEditPassphraseAgain.text()):
QtWidgets.QMessageBox.about(
self, _translate("MainWindow", "Passphrase mismatch"),
_translate(
"MainWindow",
@ -156,7 +164,7 @@ class NewAddressDialog(QtGui.QDialog):
" match. Try again.")
)
elif self.lineEditPassphrase.text() == "":
QtGui.QMessageBox.about(
QtWidgets.QMessageBox.about(
self, _translate("MainWindow", "Choose a passphrase"),
_translate(
"MainWindow", "You really do need a passphrase.")
@ -169,7 +177,7 @@ class NewAddressDialog(QtGui.QDialog):
'createDeterministicAddresses', 4, streamNumberForAddress,
"unused deterministic address",
self.spinBoxNumberOfAddressesToMake.value(),
self.lineEditPassphrase.text().toUtf8(),
ustr(self.lineEditPassphrase.text()),
self.checkBoxEighteenByteRipe.isChecked()
))
@ -180,7 +188,8 @@ class NewSubscriptionDialog(AddressDataDialog):
def __init__(self, parent=None):
super(NewSubscriptionDialog, self).__init__(parent)
widgets.load('newsubscriptiondialog.ui', self)
AddressCheckMixin.__init__(self)
self.recent = []
self._setup()
def _onSuccess(self, addressVersion, streamNumber, ripe):
if addressVersion <= 3:
@ -211,22 +220,21 @@ class NewSubscriptionDialog(AddressDataDialog):
_translate(
"MainWindow",
"Display the %n recent broadcast(s) from this address.",
None,
QtCore.QCoreApplication.CodecForTr,
count
None, count
))
class RegenerateAddressesDialog(QtGui.QDialog):
class RegenerateAddressesDialog(QtWidgets.QDialog):
"""QDialog for regenerating deterministic addresses"""
def __init__(self, parent=None):
super(RegenerateAddressesDialog, self).__init__(parent)
widgets.load('regenerateaddresses.ui', self)
self.groupBox.setTitle('')
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
class SpecialAddressBehaviorDialog(QtGui.QDialog):
class SpecialAddressBehaviorDialog(QtWidgets.QDialog):
"""
QDialog for special address behaviour (e.g. mailing list functionality)
"""
@ -234,7 +242,7 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
def __init__(self, parent=None, config=global_config):
super(SpecialAddressBehaviorDialog, self).__init__(parent)
widgets.load('specialaddressbehavior.ui', self)
self.address = parent.getCurrentAccount()
self.address = ustr(parent.getCurrentAccount())
self.parent = parent
self.config = config
@ -257,12 +265,12 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
self.radioButtonBehaviorMailingList.click()
else:
self.radioButtonBehaveNormalAddress.click()
mailingListName = config.safeGet(self.address, 'mailinglistname', '')
mailingListName = config.safeGet(
self.address, 'mailinglistname', '')
self.lineEditMailingListName.setText(
unicode(mailingListName, 'utf-8')
)
unic(ustr(mailingListName)))
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
self.show()
def accept(self):
@ -271,18 +279,18 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
if self.address_is_chan:
return
if self.radioButtonBehaveNormalAddress.isChecked():
self.config.set(str(self.address), 'mailinglist', 'false')
self.config.set(self.address, 'mailinglist', 'false')
# Set the color to either black or grey
if self.config.getboolean(self.address, 'enabled'):
self.parent.setCurrentItemColor(
QtGui.QApplication.palette().text().color()
QtWidgets.QApplication.palette().text().color()
)
else:
self.parent.setCurrentItemColor(QtGui.QColor(128, 128, 128))
else:
self.config.set(str(self.address), 'mailinglist', 'true')
self.config.set(str(self.address), 'mailinglistname', str(
self.lineEditMailingListName.text().toUtf8()))
self.config.set(self.address, 'mailinglist', 'true')
self.config.set(self.address, 'mailinglistname', ustr(
self.lineEditMailingListName.text()))
self.parent.setCurrentItemColor(
QtGui.QColor(137, 4, 177)) # magenta
self.parent.rerenderComboBoxSendFrom()
@ -291,13 +299,15 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
self.parent.rerenderMessagelistToLabels()
class EmailGatewayDialog(QtGui.QDialog):
class EmailGatewayDialog(QtWidgets.QDialog):
"""QDialog for email gateway control"""
def __init__(self, parent, config=global_config, account=None):
super(EmailGatewayDialog, self).__init__(parent)
widgets.load('emailgateway.ui', self)
self.parent = parent
self.config = config
self.data = None
if account:
self.acct = account
self.setWindowTitle(_translate(
@ -330,7 +340,7 @@ class EmailGatewayDialog(QtGui.QDialog):
else:
self.acct = MailchuckAccount(address)
self.lineEditEmail.setFocus()
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
def accept(self):
"""Accept callback"""
@ -344,7 +354,7 @@ class EmailGatewayDialog(QtGui.QDialog):
if self.radioButtonRegister.isChecked() \
or self.radioButtonRegister.isHidden():
email = str(self.lineEditEmail.text().toUtf8())
email = ustr(self.lineEditEmail.text())
self.acct.register(email)
self.config.set(self.acct.fromAddress, 'label', email)
self.config.set(self.acct.fromAddress, 'gateway', 'mailchuck')

View File

@ -1,38 +1,36 @@
"""
Address validator module.
The validator for address and passphrase QLineEdits
used in `.dialogs.NewChanDialog`.
"""
# pylint: disable=too-many-branches,too-many-arguments
# pylint: disable=too-many-arguments
from Queue import Empty
from six.moves.queue import Empty
from PyQt4 import QtGui
from unqstr import ustr
from qtpy import QtGui
from addresses import decodeAddress, addBMIfNotPresent
from bmconfigparser import config
from queues import apiAddressGeneratorReturnQueue, addressGeneratorQueue
from tr import _translate
from utils import str_chan
from .utils import str_chan
class AddressPassPhraseValidatorMixin(object):
"""Bitmessage address or passphrase validator class for Qt UI"""
def setParams(
self,
passPhraseObject=None,
addressObject=None,
feedBackObject=None,
buttonBox=None,
addressMandatory=True,
self, passPhraseObject=None, addressObject=None,
feedBackObject=None, button=None, addressMandatory=True
):
"""Initialisation"""
"""Initialization"""
self.addressObject = addressObject
self.passPhraseObject = passPhraseObject
self.feedBackObject = feedBackObject
self.buttonBox = buttonBox
self.addressMandatory = addressMandatory
self.isValid = False
# save default text
self.okButtonLabel = self.buttonBox.button(QtGui.QDialogButtonBox.Ok).text()
self.okButton = button
self.okButtonLabel = button.text()
def setError(self, string):
"""Indicate that the validation is pending or failed"""
@ -43,13 +41,13 @@ class AddressPassPhraseValidatorMixin(object):
self.feedBackObject.setStyleSheet("QLabel { color : red; }")
self.feedBackObject.setText(string)
self.isValid = False
if self.buttonBox:
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(False)
if self.okButton:
self.okButton.setEnabled(False)
if string is not None and self.feedBackObject is not None:
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(
self.okButton.setText(
_translate("AddressValidator", "Invalid"))
else:
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(
self.okButton.setText(
_translate("AddressValidator", "Validating..."))
def setOK(self, string):
@ -61,9 +59,9 @@ class AddressPassPhraseValidatorMixin(object):
self.feedBackObject.setStyleSheet("QLabel { }")
self.feedBackObject.setText(string)
self.isValid = True
if self.buttonBox:
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(self.okButtonLabel)
if self.okButton:
self.okButton.setEnabled(True)
self.okButton.setText(self.okButtonLabel)
def checkQueue(self):
"""Validator queue loop"""
@ -76,7 +74,8 @@ class AddressPassPhraseValidatorMixin(object):
while True:
try:
addressGeneratorReturnValue = apiAddressGeneratorReturnQueue.get(False)
addressGeneratorReturnValue = \
apiAddressGeneratorReturnQueue.get(False)
except Empty:
if gotOne:
break
@ -86,96 +85,120 @@ class AddressPassPhraseValidatorMixin(object):
gotOne = True
if not addressGeneratorReturnValue:
self.setError(_translate("AddressValidator", "Address already present as one of your identities."))
return (QtGui.QValidator.Intermediate, 0)
if addressGeneratorReturnValue[0] == 'chan name does not match address':
self.setError(
_translate(
self.setError(_translate(
"AddressValidator",
"Although the Bitmessage address you "
"entered was valid, it doesn't match the chan name."))
return (QtGui.QValidator.Intermediate, 0)
self.setOK(_translate("MainWindow", "Passphrase and address appear to be valid."))
"Address already present as one of your identities."
))
return
if addressGeneratorReturnValue[0] == \
'chan name does not match address':
self.setError(_translate(
"AddressValidator",
"Although the Bitmessage address you entered was valid,"
" it doesn\'t match the chan name."
))
return
self.setOK(_translate(
"MainWindow", "Passphrase and address appear to be valid."))
def returnValid(self):
"""Return the value of whether the validation was successful"""
if self.isValid:
return QtGui.QValidator.Acceptable
return QtGui.QValidator.Intermediate
return QtGui.QValidator.Acceptable if self.isValid \
else QtGui.QValidator.Intermediate
def validate(self, s, pos):
"""Top level validator method"""
if self.addressObject is None:
try:
address = ustr(self.addressObject.text())
except AttributeError:
address = None
else:
address = str(self.addressObject.text().toUtf8())
if address == "":
address = None
if self.passPhraseObject is None:
try:
passPhrase = ustr(self.passPhraseObject.text())
except AttributeError:
passPhrase = ""
else:
passPhrase = str(self.passPhraseObject.text().toUtf8())
if passPhrase == "":
passPhrase = None
# no chan name
if passPhrase is None:
self.setError(_translate("AddressValidator", "Chan name/passphrase needed. You didn't enter a chan name."))
return (QtGui.QValidator.Intermediate, pos)
if self.addressMandatory or address is not None:
# check if address already exists:
if address in config.addresses():
self.setError(_translate("AddressValidator", "Address already present as one of your identities."))
return (QtGui.QValidator.Intermediate, pos)
# version too high
if decodeAddress(address)[0] == 'versiontoohigh':
self.setError(
_translate(
if not passPhrase:
self.setError(_translate(
"AddressValidator",
"Address too new. Although that Bitmessage"
" address might be valid, its version number"
" is too new for us to handle. Perhaps you need"
" to upgrade Bitmessage."))
return (QtGui.QValidator.Intermediate, pos)
"Chan name/passphrase needed. You didn't enter a chan name."
))
return (QtGui.QValidator.Intermediate, s, pos)
if self.addressMandatory or address:
# check if address already exists:
if address in config.addresses(True):
self.setError(_translate(
"AddressValidator",
"Address already present as one of your identities."
))
return (QtGui.QValidator.Intermediate, s, pos)
status = decodeAddress(address)[0]
# version too high
if status == 'versiontoohigh':
self.setError(_translate(
"AddressValidator",
"Address too new. Although that Bitmessage address"
" might be valid, its version number is too new"
" for us to handle. Perhaps you need to upgrade"
" Bitmessage."
))
return (QtGui.QValidator.Intermediate, s, pos)
# invalid
if decodeAddress(address)[0] != 'success':
self.setError(_translate("AddressValidator", "The Bitmessage address is not valid."))
return (QtGui.QValidator.Intermediate, pos)
if status != 'success':
self.setError(_translate(
"AddressValidator",
"The Bitmessage address is not valid."
))
return (QtGui.QValidator.Intermediate, s, pos)
# this just disables the OK button without changing the feedback text
# but only if triggered by textEdited, not by clicking the Ok button
if not self.buttonBox.button(QtGui.QDialogButtonBox.Ok).hasFocus():
if not self.okButton.hasFocus():
self.setError(None)
# check through generator
if address is None:
addressGeneratorQueue.put(('createChan', 4, 1, str_chan + ' ' + str(passPhrase), passPhrase, False))
if not address:
addressGeneratorQueue.put((
'createChan', 4, 1,
str_chan + ' ' + passPhrase, passPhrase.encode("utf-8", "replace"), False
))
else:
addressGeneratorQueue.put(
('joinChan', addBMIfNotPresent(address),
"{} {}".format(str_chan, passPhrase), passPhrase, False))
addressGeneratorQueue.put((
'joinChan', addBMIfNotPresent(address),
"{} {}".format(str_chan, passPhrase), passPhrase.encode("utf-8", "replace"), False
))
if self.buttonBox.button(QtGui.QDialogButtonBox.Ok).hasFocus():
return (self.returnValid(), pos)
return (QtGui.QValidator.Intermediate, pos)
if self.okButton.hasFocus():
return (self.returnValid(), s, pos)
else:
return (QtGui.QValidator.Intermediate, s, pos)
def checkData(self):
"""Validator Qt signal interface"""
return self.validate("", 0)
return self.validate(u"", 0)
class AddressValidator(QtGui.QValidator, AddressPassPhraseValidatorMixin):
"""AddressValidator class for Qt UI"""
def __init__(self, parent=None, passPhraseObject=None, feedBackObject=None, buttonBox=None, addressMandatory=True):
def __init__(
self, parent=None, passPhraseObject=None, feedBackObject=None,
button=None, addressMandatory=True
):
super(AddressValidator, self).__init__(parent)
self.setParams(passPhraseObject, parent, feedBackObject, buttonBox, addressMandatory)
self.setParams(
passPhraseObject, parent, feedBackObject, button,
addressMandatory)
class PassPhraseValidator(QtGui.QValidator, AddressPassPhraseValidatorMixin):
"""PassPhraseValidator class for Qt UI"""
def __init__(self, parent=None, addressObject=None, feedBackObject=None, buttonBox=None, addressMandatory=False):
def __init__(
self, parent=None, addressObject=None, feedBackObject=None,
button=None, addressMandatory=False
):
super(PassPhraseValidator, self).__init__(parent)
self.setParams(parent, addressObject, feedBackObject, buttonBox, addressMandatory)
self.setParams(
parent, addressObject, feedBackObject, button,
addressMandatory)

View File

@ -7,9 +7,9 @@
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore
from qtpy import QtCore
qt_resource_data = "\
qt_resource_data = b"\
\x00\x00\x03\x66\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@ -1534,7 +1534,7 @@ qt_resource_data = "\
\x82\
"
qt_resource_name = "\
qt_resource_name = b"\
\x00\x09\
\x0c\x78\x54\x88\
\x00\x6e\
@ -1639,7 +1639,7 @@ qt_resource_name = "\
\x00\x70\x00\x6e\x00\x67\
"
qt_resource_struct = "\
qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x18\x00\x02\x00\x00\x00\x15\x00\x00\x00\x03\
@ -1666,10 +1666,15 @@ qt_resource_struct = "\
\x00\x00\x01\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x34\xdf\
"
def qInitResources():
QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
QtCore.qRegisterResourceData(
0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
def qCleanupResources():
QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
QtCore.qUnregisterResourceData(
0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
qInitResources()

View File

@ -1,59 +1,32 @@
# -*- coding: utf-8 -*-
# pylint: skip-file
# flake8: noqa
# Form implementation generated from reading ui file 'bitmessageui.ui'
#
# 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
from qtpy import QtCore, QtGui, QtWidgets
from tr import _translate
from bmconfigparser import config
from foldertree import AddressBookCompleter
from messageview import MessageView
from messagecompose import MessageCompose
import settingsmixin
from networkstatus import NetworkStatus
from blacklist import Blacklist
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None):
if n is None:
return QtGui.QApplication.translate(context, text, disambig, _encoding)
else:
return QtGui.QApplication.translate(context, text, disambig, _encoding, n)
except AttributeError:
def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None):
if n is None:
return QtGui.QApplication.translate(context, text, disambig)
else:
return QtGui.QApplication.translate(context, text, disambig, QtCore.QCoreApplication.CodecForTr, n)
from .foldertree import AddressBookCompleter
from .messageview import MessageView
from .messagecompose import MessageCompose
from bitmessageqt import settingsmixin
from .networkstatus import NetworkStatus
from .blacklist import Blacklist
from bitmessageqt import bitmessage_icons_rc
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.setObjectName("MainWindow")
MainWindow.resize(885, 580)
icon = QtGui.QIcon()
icon.addPixmap(
QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off
)
icon.addPixmap(QtGui.QPixmap(":/newPrefix/images/can-icon-24px.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
MainWindow.setTabShape(QtGui.QTabWidget.Rounded)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
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)
MainWindow.setTabShape(QtWidgets.QTabWidget.Rounded)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout_10 = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout_10.setObjectName("gridLayout_10")
self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tabWidget.sizePolicy().hasHeightForWidth())
@ -61,32 +34,30 @@ class Ui_MainWindow(object):
self.tabWidget.setMinimumSize(QtCore.QSize(0, 0))
self.tabWidget.setBaseSize(QtCore.QSize(0, 0))
font = QtGui.QFont()
base_size = QtGui.QApplication.instance().font().pointSize()
base_size = QtWidgets.QApplication.instance().font().pointSize()
font.setPointSize(int(base_size * 0.75))
self.tabWidget.setFont(font)
self.tabWidget.setTabPosition(QtGui.QTabWidget.North)
self.tabWidget.setTabShape(QtGui.QTabWidget.Rounded)
self.tabWidget.setObjectName(_fromUtf8("tabWidget"))
self.inbox = QtGui.QWidget()
self.inbox.setObjectName(_fromUtf8("inbox"))
self.gridLayout = QtGui.QGridLayout(self.inbox)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.tabWidget.setTabPosition(QtWidgets.QTabWidget.North)
self.tabWidget.setTabShape(QtWidgets.QTabWidget.Rounded)
self.tabWidget.setObjectName("tabWidget")
self.inbox = QtWidgets.QWidget()
self.inbox.setObjectName("inbox")
self.gridLayout = QtWidgets.QGridLayout(self.inbox)
self.gridLayout.setObjectName("gridLayout")
self.horizontalSplitter_3 = settingsmixin.SSplitter()
self.horizontalSplitter_3.setObjectName(_fromUtf8("horizontalSplitter_3"))
self.horizontalSplitter_3.setObjectName("horizontalSplitter_3")
self.verticalSplitter_12 = settingsmixin.SSplitter()
self.verticalSplitter_12.setObjectName(_fromUtf8("verticalSplitter_12"))
self.verticalSplitter_12.setObjectName("verticalSplitter_12")
self.verticalSplitter_12.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetYourIdentities = settingsmixin.STreeWidget(self.inbox)
self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities"))
self.treeWidgetYourIdentities.setObjectName("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
)
icon1.addPixmap(QtGui.QPixmap(":/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 = QtWidgets.QPushButton(self.inbox)
self.pushButtonNewAddress.setObjectName("pushButtonNewAddress")
self.pushButtonNewAddress.resize(200, self.pushButtonNewAddress.height())
self.verticalSplitter_12.addWidget(self.pushButtonNewAddress)
self.verticalSplitter_12.setStretchFactor(0, 1)
@ -96,21 +67,21 @@ class Ui_MainWindow(object):
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.setObjectName("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.horizontalSplitterSearch = QtWidgets.QSplitter()
self.horizontalSplitterSearch.setObjectName("horizontalSplitterSearch")
self.inboxSearchLineEdit = QtWidgets.QLineEdit(self.inbox)
self.inboxSearchLineEdit.setObjectName("inboxSearchLineEdit")
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.inboxSearchOption = QtWidgets.QComboBox(self.inbox)
self.inboxSearchOption.setObjectName("inboxSearchOption")
self.inboxSearchOption.addItem("")
self.inboxSearchOption.addItem("")
self.inboxSearchOption.addItem("")
self.inboxSearchOption.addItem("")
self.inboxSearchOption.addItem("")
self.inboxSearchOption.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.inboxSearchOption.setCurrentIndex(3)
self.horizontalSplitterSearch.addWidget(self.inboxSearchOption)
self.horizontalSplitterSearch.handle(1).setEnabled(False)
@ -118,21 +89,21 @@ class Ui_MainWindow(object):
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.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidgetInbox.setAlternatingRowColors(True)
self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetInbox.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetInbox.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.tableWidgetInbox.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableWidgetInbox.setWordWrap(False)
self.tableWidgetInbox.setObjectName(_fromUtf8("tableWidgetInbox"))
self.tableWidgetInbox.setObjectName("tableWidgetInbox")
self.tableWidgetInbox.setColumnCount(4)
self.tableWidgetInbox.setRowCount(0)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(3, item)
self.tableWidgetInbox.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInbox.horizontalHeader().setDefaultSectionSize(200)
@ -146,7 +117,7 @@ class Ui_MainWindow(object):
self.textEditInboxMessage = MessageView(self.inbox)
self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessage.setReadOnly(True)
self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage"))
self.textEditInboxMessage.setObjectName("textEditInboxMessage")
self.verticalSplitter_7.addWidget(self.textEditInboxMessage)
self.verticalSplitter_7.setStretchFactor(0, 0)
self.verticalSplitter_7.setStretchFactor(1, 1)
@ -162,52 +133,51 @@ class Ui_MainWindow(object):
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_7 = QtGui.QGridLayout(self.send)
self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7"))
icon2.addPixmap(QtGui.QPixmap(":/newPrefix/images/inbox.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.inbox, icon2, "")
self.send = QtWidgets.QWidget()
self.send.setObjectName("send")
self.gridLayout_7 = QtWidgets.QGridLayout(self.send)
self.gridLayout_7.setObjectName("gridLayout_7")
self.horizontalSplitter = settingsmixin.SSplitter()
self.horizontalSplitter.setObjectName(_fromUtf8("horizontalSplitter"))
self.horizontalSplitter.setObjectName("horizontalSplitter")
self.verticalSplitter_2 = settingsmixin.SSplitter()
self.verticalSplitter_2.setObjectName(_fromUtf8("verticalSplitter_2"))
self.verticalSplitter_2.setObjectName("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.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.tableWidgetAddressBook.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableWidgetAddressBook.setObjectName("tableWidgetAddressBook")
self.tableWidgetAddressBook.setColumnCount(2)
self.tableWidgetAddressBook.setRowCount(0)
self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height())
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
icon3 = QtGui.QIcon()
icon3.addPixmap(
QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off
)
icon3.addPixmap(QtGui.QPixmap(":/newPrefix/images/addressbook.png"), QtGui.QIcon.Selected, QtGui.QIcon.Off)
item.setIcon(icon3)
self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.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.tableWidgetAddressBook.setWordWrap(False)
self.verticalSplitter_2.addWidget(self.tableWidgetAddressBook)
self.addressBookCompleter = AddressBookCompleter()
self.addressBookCompleter.setCompletionMode(QtGui.QCompleter.PopupCompletion)
self.addressBookCompleter.setCompletionMode(QtWidgets.QCompleter.PopupCompletion)
self.addressBookCompleter.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.addressBookCompleterModel = QtGui.QStringListModel()
self.addressBookCompleterModel = QtCore.QStringListModel()
self.addressBookCompleter.setModel(self.addressBookCompleterModel)
self.pushButtonAddAddressBook = QtGui.QPushButton(self.send)
self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook"))
self.pushButtonAddAddressBook = QtWidgets.QPushButton(self.send)
self.pushButtonAddAddressBook.setObjectName("pushButtonAddAddressBook")
self.pushButtonAddAddressBook.resize(200, self.pushButtonAddAddressBook.height())
self.verticalSplitter_2.addWidget(self.pushButtonAddAddressBook)
self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send)
self.pushButtonFetchNamecoinID = QtWidgets.QPushButton(self.send)
self.pushButtonFetchNamecoinID.resize(200, self.pushButtonFetchNamecoinID.height())
self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID"))
self.pushButtonFetchNamecoinID.setObjectName("pushButtonFetchNamecoinID")
self.verticalSplitter_2.addWidget(self.pushButtonFetchNamecoinID)
self.verticalSplitter_2.setStretchFactor(0, 1)
self.verticalSplitter_2.setStretchFactor(1, 0)
@ -219,45 +189,45 @@ class Ui_MainWindow(object):
self.verticalSplitter_2.handle(2).setEnabled(False)
self.horizontalSplitter.addWidget(self.verticalSplitter_2)
self.verticalSplitter = settingsmixin.SSplitter()
self.verticalSplitter.setObjectName(_fromUtf8("verticalSplitter"))
self.verticalSplitter.setObjectName("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.tabWidgetSend = QtWidgets.QTabWidget(self.send)
self.tabWidgetSend.setObjectName("tabWidgetSend")
self.sendDirect = QtWidgets.QWidget()
self.sendDirect.setObjectName("sendDirect")
self.gridLayout_8 = QtWidgets.QGridLayout(self.sendDirect)
self.gridLayout_8.setObjectName("gridLayout_8")
self.verticalSplitter_5 = settingsmixin.SSplitter()
self.verticalSplitter_5.setObjectName(_fromUtf8("verticalSplitter_5"))
self.verticalSplitter_5.setObjectName("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 = QtWidgets.QGridLayout()
self.gridLayout_2.setObjectName("gridLayout_2")
self.label_3 = QtWidgets.QLabel(self.sendDirect)
self.label_3.setObjectName("label_3")
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.label_2 = QtWidgets.QLabel(self.sendDirect)
self.label_2.setObjectName("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.lineEditSubject = QtWidgets.QLineEdit(self.sendDirect)
self.lineEditSubject.setText("")
self.lineEditSubject.setObjectName("lineEditSubject")
self.gridLayout_2.addWidget(self.lineEditSubject, 2, 1, 1, 1)
self.label = QtGui.QLabel(self.sendDirect)
self.label.setObjectName(_fromUtf8("label"))
self.label = QtWidgets.QLabel(self.sendDirect)
self.label.setObjectName("label")
self.gridLayout_2.addWidget(self.label, 1, 0, 1, 1)
self.comboBoxSendFrom = QtGui.QComboBox(self.sendDirect)
self.comboBoxSendFrom = QtWidgets.QComboBox(self.sendDirect)
self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0))
self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom"))
self.comboBoxSendFrom.setObjectName("comboBoxSendFrom")
self.gridLayout_2.addWidget(self.comboBoxSendFrom, 0, 1, 1, 1)
self.lineEditTo = QtGui.QLineEdit(self.sendDirect)
self.lineEditTo.setObjectName(_fromUtf8("lineEditTo"))
self.lineEditTo = QtWidgets.QLineEdit(self.sendDirect)
self.lineEditTo.setObjectName("lineEditTo")
self.gridLayout_2.addWidget(self.lineEditTo, 1, 1, 1, 1)
self.lineEditTo.setCompleter(self.addressBookCompleter)
self.gridLayout_2_Widget = QtGui.QWidget()
self.gridLayout_2_Widget = QtWidgets.QWidget()
self.gridLayout_2_Widget.setLayout(self.gridLayout_2)
self.verticalSplitter_5.addWidget(self.gridLayout_2_Widget)
self.textEditMessage = MessageCompose(self.sendDirect)
self.textEditMessage.setObjectName(_fromUtf8("textEditMessage"))
self.textEditMessage.setObjectName("textEditMessage")
self.verticalSplitter_5.addWidget(self.textEditMessage)
self.verticalSplitter_5.setStretchFactor(0, 0)
self.verticalSplitter_5.setStretchFactor(1, 1)
@ -265,35 +235,35 @@ class Ui_MainWindow(object):
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.tabWidgetSend.addTab(self.sendDirect, "")
self.sendBroadcast = QtWidgets.QWidget()
self.sendBroadcast.setObjectName("sendBroadcast")
self.gridLayout_9 = QtWidgets.QGridLayout(self.sendBroadcast)
self.gridLayout_9.setObjectName("gridLayout_9")
self.verticalSplitter_6 = settingsmixin.SSplitter()
self.verticalSplitter_6.setObjectName(_fromUtf8("verticalSplitter_6"))
self.verticalSplitter_6.setObjectName("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 = QtWidgets.QGridLayout()
self.gridLayout_5.setObjectName("gridLayout_5")
self.label_8 = QtWidgets.QLabel(self.sendBroadcast)
self.label_8.setObjectName("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.lineEditSubjectBroadcast = QtWidgets.QLineEdit(self.sendBroadcast)
self.lineEditSubjectBroadcast.setText("")
self.lineEditSubjectBroadcast.setObjectName("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.label_7 = QtWidgets.QLabel(self.sendBroadcast)
self.label_7.setObjectName("label_7")
self.gridLayout_5.addWidget(self.label_7, 1, 0, 1, 1)
self.comboBoxSendFromBroadcast = QtGui.QComboBox(self.sendBroadcast)
self.comboBoxSendFromBroadcast = QtWidgets.QComboBox(self.sendBroadcast)
self.comboBoxSendFromBroadcast.setMinimumSize(QtCore.QSize(300, 0))
self.comboBoxSendFromBroadcast.setObjectName(_fromUtf8("comboBoxSendFromBroadcast"))
self.comboBoxSendFromBroadcast.setObjectName("comboBoxSendFromBroadcast")
self.gridLayout_5.addWidget(self.comboBoxSendFromBroadcast, 0, 1, 1, 1)
self.gridLayout_5_Widget = QtGui.QWidget()
self.gridLayout_5_Widget = QtWidgets.QWidget()
self.gridLayout_5_Widget.setLayout(self.gridLayout_5)
self.verticalSplitter_6.addWidget(self.gridLayout_5_Widget)
self.textEditMessageBroadcast = MessageCompose(self.sendBroadcast)
self.textEditMessageBroadcast.setObjectName(_fromUtf8("textEditMessageBroadcast"))
self.textEditMessageBroadcast.setObjectName("textEditMessageBroadcast")
self.verticalSplitter_6.addWidget(self.textEditMessageBroadcast)
self.verticalSplitter_6.setStretchFactor(0, 0)
self.verticalSplitter_6.setStretchFactor(1, 1)
@ -301,15 +271,15 @@ class Ui_MainWindow(object):
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.tabWidgetSend.addTab(self.sendBroadcast, "")
self.verticalSplitter.addWidget(self.tabWidgetSend)
self.tTLContainer = QtGui.QWidget()
self.tTLContainer.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
self.horizontalLayout_5 = QtGui.QHBoxLayout()
self.tTLContainer = QtWidgets.QWidget()
self.tTLContainer.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.tTLContainer.setLayout(self.horizontalLayout_5)
self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5"))
self.pushButtonTTL = QtGui.QPushButton(self.send)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.pushButtonTTL = QtWidgets.QPushButton(self.send)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButtonTTL.sizePolicy().hasHeightForWidth())
@ -329,29 +299,29 @@ class Ui_MainWindow(object):
font.setUnderline(True)
self.pushButtonTTL.setFont(font)
self.pushButtonTTL.setFlat(True)
self.pushButtonTTL.setObjectName(_fromUtf8("pushButtonTTL"))
self.pushButtonTTL.setObjectName("pushButtonTTL")
self.horizontalLayout_5.addWidget(self.pushButtonTTL, 0, QtCore.Qt.AlignRight)
self.horizontalSliderTTL = QtGui.QSlider(self.send)
self.horizontalSliderTTL = QtWidgets.QSlider(self.send)
self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(70, 0))
self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal)
self.horizontalSliderTTL.setInvertedAppearance(False)
self.horizontalSliderTTL.setInvertedControls(False)
self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL"))
self.horizontalSliderTTL.setObjectName("horizontalSliderTTL")
self.horizontalLayout_5.addWidget(self.horizontalSliderTTL, 0, QtCore.Qt.AlignLeft)
self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
self.labelHumanFriendlyTTLDescription = QtWidgets.QLabel(self.send)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
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.labelHumanFriendlyTTLDescription.setObjectName("labelHumanFriendlyTTLDescription")
self.horizontalLayout_5.addWidget(self.labelHumanFriendlyTTLDescription, 1, QtCore.Qt.AlignLeft)
self.pushButtonClear = QtGui.QPushButton(self.send)
self.pushButtonClear.setObjectName(_fromUtf8("pushButtonClear"))
self.pushButtonClear = QtWidgets.QPushButton(self.send)
self.pushButtonClear.setObjectName("pushButtonClear")
self.horizontalLayout_5.addWidget(self.pushButtonClear, 0, QtCore.Qt.AlignRight)
self.pushButtonSend = QtGui.QPushButton(self.send)
self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend"))
self.pushButtonSend = QtWidgets.QPushButton(self.send)
self.pushButtonSend.setObjectName("pushButtonSend")
self.horizontalLayout_5.addWidget(self.pushButtonSend, 0, QtCore.Qt.AlignRight)
self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(105, self.pushButtonSend.height()))
self.verticalSplitter.addWidget(self.tTLContainer)
@ -368,31 +338,29 @@ class Ui_MainWindow(object):
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/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_3 = QtGui.QGridLayout(self.subscriptions)
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
icon4.addPixmap(QtGui.QPixmap(":/newPrefix/images/send.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.send, icon4, "")
self.subscriptions = QtWidgets.QWidget()
self.subscriptions.setObjectName("subscriptions")
self.gridLayout_3 = QtWidgets.QGridLayout(self.subscriptions)
self.gridLayout_3.setObjectName("gridLayout_3")
self.horizontalSplitter_4 = settingsmixin.SSplitter()
self.horizontalSplitter_4.setObjectName(_fromUtf8("horizontalSplitter_4"))
self.horizontalSplitter_4.setObjectName("horizontalSplitter_4")
self.verticalSplitter_3 = settingsmixin.SSplitter()
self.verticalSplitter_3.setObjectName(_fromUtf8("verticalSplitter_3"))
self.verticalSplitter_3.setObjectName("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.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.treeWidgetSubscriptions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.treeWidgetSubscriptions.setObjectName("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
)
icon5.addPixmap(QtGui.QPixmap(":/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.pushButtonAddSubscription = QtWidgets.QPushButton(self.subscriptions)
self.pushButtonAddSubscription.setObjectName("pushButtonAddSubscription")
self.pushButtonAddSubscription.resize(200, self.pushButtonAddSubscription.height())
self.verticalSplitter_3.addWidget(self.pushButtonAddSubscription)
self.verticalSplitter_3.setStretchFactor(0, 1)
@ -402,20 +370,20 @@ class Ui_MainWindow(object):
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.setObjectName("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 = QtWidgets.QSplitter()
self.horizontalSplitter_2.setObjectName("horizontalSplitter_2")
self.inboxSearchLineEditSubscriptions = QtWidgets.QLineEdit(self.subscriptions)
self.inboxSearchLineEditSubscriptions.setObjectName("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.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
self.inboxSearchOptionSubscriptions = QtWidgets.QComboBox(self.subscriptions)
self.inboxSearchOptionSubscriptions.setObjectName("inboxSearchOptionSubscriptions")
self.inboxSearchOptionSubscriptions.addItem("")
self.inboxSearchOptionSubscriptions.addItem("")
self.inboxSearchOptionSubscriptions.addItem("")
self.inboxSearchOptionSubscriptions.addItem("")
self.inboxSearchOptionSubscriptions.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.inboxSearchOptionSubscriptions.setCurrentIndex(2)
self.horizontalSplitter_2.addWidget(self.inboxSearchOptionSubscriptions)
self.horizontalSplitter_2.handle(1).setEnabled(False)
@ -423,21 +391,21 @@ class Ui_MainWindow(object):
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.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidgetInboxSubscriptions.setAlternatingRowColors(True)
self.tableWidgetInboxSubscriptions.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetInboxSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetInboxSubscriptions.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.tableWidgetInboxSubscriptions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableWidgetInboxSubscriptions.setWordWrap(False)
self.tableWidgetInboxSubscriptions.setObjectName(_fromUtf8("tableWidgetInboxSubscriptions"))
self.tableWidgetInboxSubscriptions.setObjectName("tableWidgetInboxSubscriptions")
self.tableWidgetInboxSubscriptions.setColumnCount(4)
self.tableWidgetInboxSubscriptions.setRowCount(0)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(3, item)
self.tableWidgetInboxSubscriptions.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInboxSubscriptions.horizontalHeader().setDefaultSectionSize(200)
@ -451,7 +419,7 @@ class Ui_MainWindow(object):
self.textEditInboxMessageSubscriptions = MessageView(self.subscriptions)
self.textEditInboxMessageSubscriptions.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessageSubscriptions.setReadOnly(True)
self.textEditInboxMessageSubscriptions.setObjectName(_fromUtf8("textEditInboxMessageSubscriptions"))
self.textEditInboxMessageSubscriptions.setObjectName("textEditInboxMessageSubscriptions")
self.verticalSplitter_4.addWidget(self.textEditInboxMessageSubscriptions)
self.verticalSplitter_4.setStretchFactor(0, 0)
self.verticalSplitter_4.setStretchFactor(1, 1)
@ -467,35 +435,31 @@ class Ui_MainWindow(object):
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/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"))
icon6.addPixmap(QtGui.QPixmap(":/newPrefix/images/subscriptions.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.subscriptions, icon6, "")
self.chans = QtWidgets.QWidget()
self.chans.setObjectName("chans")
self.gridLayout_4 = QtWidgets.QGridLayout(self.chans)
self.gridLayout_4.setObjectName("gridLayout_4")
self.horizontalSplitter_7 = settingsmixin.SSplitter()
self.horizontalSplitter_7.setObjectName(_fromUtf8("horizontalSplitter_7"))
self.horizontalSplitter_7.setObjectName("horizontalSplitter_7")
self.verticalSplitter_17 = settingsmixin.SSplitter()
self.verticalSplitter_17.setObjectName(_fromUtf8("verticalSplitter_17"))
self.verticalSplitter_17.setObjectName("verticalSplitter_17")
self.verticalSplitter_17.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetChans = settingsmixin.STreeWidget(self.chans)
self.treeWidgetChans.setFrameShadow(QtGui.QFrame.Sunken)
self.treeWidgetChans.setFrameShadow(QtWidgets.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.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.treeWidgetChans.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.treeWidgetChans.setObjectName("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
)
icon7.addPixmap(QtGui.QPixmap(":/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 = QtWidgets.QPushButton(self.chans)
self.pushButtonAddChan.setObjectName("pushButtonAddChan")
self.pushButtonAddChan.resize(200, self.pushButtonAddChan.height())
self.verticalSplitter_17.addWidget(self.pushButtonAddChan)
self.verticalSplitter_17.setStretchFactor(0, 1)
@ -505,21 +469,21 @@ class Ui_MainWindow(object):
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.setObjectName("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 = QtWidgets.QSplitter()
self.horizontalSplitter_6.setObjectName("horizontalSplitter_6")
self.inboxSearchLineEditChans = QtWidgets.QLineEdit(self.chans)
self.inboxSearchLineEditChans.setObjectName("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.inboxSearchOptionChans = QtWidgets.QComboBox(self.chans)
self.inboxSearchOptionChans.setObjectName("inboxSearchOptionChans")
self.inboxSearchOptionChans.addItem("")
self.inboxSearchOptionChans.addItem("")
self.inboxSearchOptionChans.addItem("")
self.inboxSearchOptionChans.addItem("")
self.inboxSearchOptionChans.addItem("")
self.inboxSearchOptionChans.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.inboxSearchOptionChans.setCurrentIndex(3)
self.horizontalSplitter_6.addWidget(self.inboxSearchOptionChans)
self.horizontalSplitter_6.handle(1).setEnabled(False)
@ -527,21 +491,21 @@ class Ui_MainWindow(object):
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.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidgetInboxChans.setAlternatingRowColors(True)
self.tableWidgetInboxChans.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidgetInboxChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.tableWidgetInboxChans.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.tableWidgetInboxChans.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableWidgetInboxChans.setWordWrap(False)
self.tableWidgetInboxChans.setObjectName(_fromUtf8("tableWidgetInboxChans"))
self.tableWidgetInboxChans.setObjectName("tableWidgetInboxChans")
self.tableWidgetInboxChans.setColumnCount(4)
self.tableWidgetInboxChans.setRowCount(0)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem()
item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(3, item)
self.tableWidgetInboxChans.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInboxChans.horizontalHeader().setDefaultSectionSize(200)
@ -555,7 +519,7 @@ class Ui_MainWindow(object):
self.textEditInboxMessageChans = MessageView(self.chans)
self.textEditInboxMessageChans.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessageChans.setReadOnly(True)
self.textEditInboxMessageChans.setObjectName(_fromUtf8("textEditInboxMessageChans"))
self.textEditInboxMessageChans.setObjectName("textEditInboxMessageChans")
self.verticalSplitter_8.addWidget(self.textEditInboxMessageChans)
self.verticalSplitter_8.setStretchFactor(0, 0)
self.verticalSplitter_8.setStretchFactor(1, 1)
@ -571,10 +535,8 @@ class Ui_MainWindow(object):
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(""))
icon8.addPixmap(QtGui.QPixmap(":/newPrefix/images/can-icon-16px.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.tabWidget.addTab(self.chans, icon8, "")
self.blackwhitelist = Blacklist()
self.tabWidget.addTab(self.blackwhitelist, QtGui.QIcon(":/newPrefix/images/blacklist.png"), "")
# Initialize the Blacklist or Whitelist
@ -586,62 +548,62 @@ class Ui_MainWindow(object):
self.tabWidget.addTab(self.networkstatus, QtGui.QIcon(":/newPrefix/images/networkstatus.png"), "")
self.gridLayout_10.addWidget(self.tabWidget, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar = QtWidgets.QMenuBar(MainWindow)
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"))
self.menuSettings = QtGui.QMenu(self.menubar)
self.menuSettings.setObjectName(_fromUtf8("menuSettings"))
self.menuHelp = QtGui.QMenu(self.menubar)
self.menuHelp.setObjectName(_fromUtf8("menuHelp"))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
self.menuSettings = QtWidgets.QMenu(self.menubar)
self.menuSettings.setObjectName("menuSettings")
self.menuHelp = QtWidgets.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setMaximumSize(QtCore.QSize(16777215, 22))
self.statusbar.setObjectName(_fromUtf8("statusbar"))
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionImport_keys = QtGui.QAction(MainWindow)
self.actionImport_keys.setObjectName(_fromUtf8("actionImport_keys"))
self.actionManageKeys = QtGui.QAction(MainWindow)
self.actionImport_keys = QtWidgets.QAction(MainWindow)
self.actionImport_keys.setObjectName("actionImport_keys")
self.actionManageKeys = QtWidgets.QAction(MainWindow)
self.actionManageKeys.setCheckable(False)
self.actionManageKeys.setEnabled(True)
icon = QtGui.QIcon.fromTheme(_fromUtf8("dialog-password"))
icon = QtGui.QIcon.fromTheme("dialog-password")
self.actionManageKeys.setIcon(icon)
self.actionManageKeys.setObjectName(_fromUtf8("actionManageKeys"))
self.actionNetworkSwitch = QtGui.QAction(MainWindow)
self.actionNetworkSwitch.setObjectName(_fromUtf8("actionNetworkSwitch"))
self.actionExit = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("application-exit"))
self.actionManageKeys.setObjectName("actionManageKeys")
self.actionNetworkSwitch = QtWidgets.QAction(MainWindow)
self.actionNetworkSwitch.setObjectName("actionNetworkSwitch")
self.actionExit = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("application-exit")
self.actionExit.setIcon(icon)
self.actionExit.setObjectName(_fromUtf8("actionExit"))
self.actionHelp = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("help-contents"))
self.actionExit.setObjectName("actionExit")
self.actionHelp = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("help-contents")
self.actionHelp.setIcon(icon)
self.actionHelp.setObjectName(_fromUtf8("actionHelp"))
self.actionSupport = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("help-support"))
self.actionHelp.setObjectName("actionHelp")
self.actionSupport = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("help-support")
self.actionSupport.setIcon(icon)
self.actionSupport.setObjectName(_fromUtf8("actionSupport"))
self.actionAbout = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("help-about"))
self.actionSupport.setObjectName("actionSupport")
self.actionAbout = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("help-about")
self.actionAbout.setIcon(icon)
self.actionAbout.setObjectName(_fromUtf8("actionAbout"))
self.actionSettings = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("document-properties"))
self.actionAbout.setObjectName("actionAbout")
self.actionSettings = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("document-properties")
self.actionSettings.setIcon(icon)
self.actionSettings.setObjectName(_fromUtf8("actionSettings"))
self.actionRegenerateDeterministicAddresses = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("view-refresh"))
self.actionSettings.setObjectName("actionSettings")
self.actionRegenerateDeterministicAddresses = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("view-refresh")
self.actionRegenerateDeterministicAddresses.setIcon(icon)
self.actionRegenerateDeterministicAddresses.setObjectName(_fromUtf8("actionRegenerateDeterministicAddresses"))
self.actionDeleteAllTrashedMessages = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("user-trash"))
self.actionRegenerateDeterministicAddresses.setObjectName("actionRegenerateDeterministicAddresses")
self.actionDeleteAllTrashedMessages = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("user-trash")
self.actionDeleteAllTrashedMessages.setIcon(icon)
self.actionDeleteAllTrashedMessages.setObjectName(_fromUtf8("actionDeleteAllTrashedMessages"))
self.actionJoinChan = QtGui.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme(_fromUtf8("contact-new"))
self.actionDeleteAllTrashedMessages.setObjectName("actionDeleteAllTrashedMessages")
self.actionJoinChan = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("contact-new")
self.actionJoinChan.setIcon(icon)
self.actionJoinChan.setObjectName(_fromUtf8("actionJoinChan"))
self.actionJoinChan.setObjectName("actionJoinChan")
self.menuFile.addAction(self.actionManageKeys)
self.menuFile.addAction(self.actionDeleteAllTrashedMessages)
self.menuFile.addAction(self.actionRegenerateDeterministicAddresses)
@ -672,11 +634,11 @@ class Ui_MainWindow(object):
# Popup menu actions container for the Sent page
# pylint: disable=attribute-defined-outside-init
self.sentContextMenuToolbar = QtGui.QToolBar()
self.sentContextMenuToolbar = QtWidgets.QToolBar()
# Popup menu actions container for chans tree
self.addressContextMenuToolbar = QtGui.QToolBar()
self.addressContextMenuToolbar = QtWidgets.QToolBar()
# Popup menu actions container for subscriptions tree
self.subscriptionsContextMenuToolbar = QtGui.QToolBar()
self.subscriptionsContextMenuToolbar = QtWidgets.QToolBar()
def updateNetworkSwitchMenuLabel(self, dontconnect=None):
if dontconnect is None:
@ -733,9 +695,7 @@ class Ui_MainWindow(object):
hours = int(config.getint('bitmessagesettings', 'ttl') / 60 / 60)
except:
pass
self.labelHumanFriendlyTTLDescription.setText(
_translate("MainWindow", "%n hour(s)", None, QtCore.QCoreApplication.CodecForTr, hours)
)
self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "%n hour(s)", None, hours))
self.pushButtonClear.setText(_translate("MainWindow", "Clear", None))
self.pushButtonSend.setText(_translate("MainWindow", "Send", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None))
@ -810,7 +770,7 @@ class Ui_MainWindow(object):
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app = QtWidgets.QApplication(sys.argv)
MainWindow = settingsmixin.SMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)

View File

@ -1,42 +1,44 @@
from PyQt4 import QtCore, QtGui
from unqstr import ustr, unic
from dbcompat import dbstr
from qtpy import QtCore, QtGui, QtWidgets
import widgets
from bitmessageqt import widgets
from addresses import addBMIfNotPresent
from bmconfigparser import config
from dialogs import AddAddressDialog
from .dialogs import AddAddressDialog
from helper_sql import sqlExecute, sqlQuery
from queues import UISignalQueue
from retranslateui import RetranslateMixin
from .retranslateui import RetranslateMixin
from tr import _translate
from uisignaler import UISignaler
from utils import avatarize
from .uisignaler import UISignaler
from .utils import avatarize
class Blacklist(QtGui.QWidget, RetranslateMixin):
class Blacklist(QtWidgets.QWidget, RetranslateMixin):
def __init__(self, parent=None):
super(Blacklist, self).__init__(parent)
widgets.load('blacklist.ui', self)
QtCore.QObject.connect(self.radioButtonBlacklist, QtCore.SIGNAL(
"clicked()"), self.click_radioButtonBlacklist)
QtCore.QObject.connect(self.radioButtonWhitelist, QtCore.SIGNAL(
"clicked()"), self.click_radioButtonWhitelist)
QtCore.QObject.connect(self.pushButtonAddBlacklist, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonAddBlacklist)
self.radioButtonBlacklist.clicked.connect(
self.click_radioButtonBlacklist)
self.radioButtonWhitelist.clicked.connect(
self.click_radioButtonWhitelist)
self.pushButtonAddBlacklist.clicked.connect(
self.click_pushButtonAddBlacklist)
self.init_blacklist_popup_menu()
# Initialize blacklist
QtCore.QObject.connect(self.tableWidgetBlacklist, QtCore.SIGNAL(
"itemChanged(QTableWidgetItem *)"), self.tableWidgetBlacklistItemChanged)
self.tableWidgetBlacklist.itemChanged.connect(
self.tableWidgetBlacklistItemChanged)
# Set the icon sizes for the identicons
identicon_size = 3*7
self.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size))
identicon_size = 3 * 7
self.tableWidgetBlacklist.setIconSize(
QtCore.QSize(identicon_size, identicon_size))
self.UISignalThread = UISignaler.get()
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"rerenderBlackWhiteList()"), self.rerenderBlackWhiteList)
self.UISignalThread.rerenderBlackWhiteList.connect(
self.rerenderBlackWhiteList)
def click_radioButtonBlacklist(self):
if config.get('bitmessagesettings', 'blackwhitelist') == 'white':
@ -59,30 +61,30 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
if self.NewBlacklistDialogInstance.exec_():
if self.NewBlacklistDialogInstance.labelAddressCheck.text() == \
_translate("MainWindow", "Address is valid."):
address = addBMIfNotPresent(str(
address = addBMIfNotPresent(ustr(
self.NewBlacklistDialogInstance.lineEditAddress.text()))
# First we must check to see if the address is already in the
# address book. The user cannot add it again or else it will
# cause problems when updating and deleting the entry.
t = (address,)
t = (dbstr(address),)
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sql = '''select * from blacklist where address=?'''
else:
sql = '''select * from whitelist where address=?'''
queryreturn = sqlQuery(sql,*t)
queryreturn = sqlQuery(sql, *t)
if queryreturn == []:
self.tableWidgetBlacklist.setSortingEnabled(False)
self.tableWidgetBlacklist.insertRow(0)
newItem = QtGui.QTableWidgetItem(unicode(
self.NewBlacklistDialogInstance.lineEditLabel.text().toUtf8(), 'utf-8'))
newItem = QtGui.QTableWidgetItem(unic(ustr(
self.NewBlacklistDialogInstance.lineEditLabel.text())))
newItem.setIcon(avatarize(address))
self.tableWidgetBlacklist.setItem(0, 0, newItem)
newItem = QtGui.QTableWidgetItem(address)
newItem = QtWidgets.QTableWidgetItem(address)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.tableWidgetBlacklist.setItem(0, 1, newItem)
self.tableWidgetBlacklist.setSortingEnabled(True)
t = (str(self.NewBlacklistDialogInstance.lineEditLabel.text().toUtf8()), address, True)
t = (dbstr(self.NewBlacklistDialogInstance.lineEditLabel.text()), dbstr(address), True)
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sql = '''INSERT INTO blacklist VALUES (?,?,?)'''
else:
@ -108,17 +110,17 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
def tableWidgetBlacklistItemChanged(self, item):
if item.column() == 0:
addressitem = self.tableWidgetBlacklist.item(item.row(), 1)
if isinstance(addressitem, QtGui.QTableWidgetItem):
if isinstance(addressitem, QtWidgets.QTableWidgetItem):
if self.radioButtonBlacklist.isChecked():
sqlExecute('''UPDATE blacklist SET label=? WHERE address=?''',
str(item.text()), str(addressitem.text()))
dbstr(item.text()), dbstr(addressitem.text()))
else:
sqlExecute('''UPDATE whitelist SET label=? WHERE address=?''',
str(item.text()), str(addressitem.text()))
dbstr(item.text()), dbstr(addressitem.text()))
def init_blacklist_popup_menu(self, connectSignal=True):
# Popup menu for the Blacklist page
self.blacklistContextMenuToolbar = QtGui.QToolBar()
self.blacklistContextMenuToolbar = QtWidgets.QToolBar()
# Actions
self.actionBlacklistNew = self.blacklistContextMenuToolbar.addAction(
_translate(
@ -143,10 +145,9 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
self.tableWidgetBlacklist.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.tableWidgetBlacklist, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.tableWidgetBlacklist.customContextMenuRequested.connect(
self.on_context_menuBlacklist)
self.popMenuBlacklist = QtGui.QMenu(self)
self.popMenuBlacklist = QtWidgets.QMenu(self)
# self.popMenuBlacklist.addAction( self.actionBlacklistNew )
self.popMenuBlacklist.addAction(self.actionBlacklistDelete)
self.popMenuBlacklist.addSeparator()
@ -171,17 +172,19 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
self.tableWidgetBlacklist.setSortingEnabled(False)
for row in queryreturn:
label, address, enabled = row
label = label.decode("utf-8", "replace")
address = address.decode("utf-8", "replace")
self.tableWidgetBlacklist.insertRow(0)
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
newItem = QtWidgets.QTableWidgetItem(unic(ustr(label)))
if not enabled:
newItem.setTextColor(QtGui.QColor(128, 128, 128))
newItem.setForeground(QtGui.QColor(128, 128, 128))
newItem.setIcon(avatarize(address))
self.tableWidgetBlacklist.setItem(0, 0, newItem)
newItem = QtGui.QTableWidgetItem(address)
newItem = QtWidgets.QTableWidgetItem(address)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not enabled:
newItem.setTextColor(QtGui.QColor(128, 128, 128))
newItem.setForeground(QtGui.QColor(128, 128, 128))
self.tableWidgetBlacklist.setItem(0, 1, newItem)
self.tableWidgetBlacklist.setSortingEnabled(True)
@ -191,26 +194,26 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
def on_action_BlacklistDelete(self):
currentRow = self.tableWidgetBlacklist.currentRow()
labelAtCurrentRow = self.tableWidgetBlacklist.item(
currentRow, 0).text().toUtf8()
labelAtCurrentRow = ustr(self.tableWidgetBlacklist.item(
currentRow, 0).text())
addressAtCurrentRow = self.tableWidgetBlacklist.item(
currentRow, 1).text()
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sqlExecute(
'''DELETE FROM blacklist WHERE label=? AND address=?''',
str(labelAtCurrentRow), str(addressAtCurrentRow))
dbstr(labelAtCurrentRow), dbstr(addressAtCurrentRow))
else:
sqlExecute(
'''DELETE FROM whitelist WHERE label=? AND address=?''',
str(labelAtCurrentRow), str(addressAtCurrentRow))
dbstr(labelAtCurrentRow), dbstr(addressAtCurrentRow))
self.tableWidgetBlacklist.removeRow(currentRow)
def on_action_BlacklistClipboard(self):
currentRow = self.tableWidgetBlacklist.currentRow()
addressAtCurrentRow = self.tableWidgetBlacklist.item(
currentRow, 1).text()
clipboard = QtGui.QApplication.clipboard()
clipboard.setText(str(addressAtCurrentRow))
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(ustr(addressAtCurrentRow))
def on_context_menuBlacklist(self, point):
self.popMenuBlacklist.exec_(
@ -220,33 +223,33 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
currentRow = self.tableWidgetBlacklist.currentRow()
addressAtCurrentRow = self.tableWidgetBlacklist.item(
currentRow, 1).text()
self.tableWidgetBlacklist.item(
currentRow, 0).setTextColor(QtGui.QApplication.palette().text().color())
self.tableWidgetBlacklist.item(
currentRow, 1).setTextColor(QtGui.QApplication.palette().text().color())
self.tableWidgetBlacklist.item(currentRow, 0).setForeground(
QtWidgets.QApplication.palette().text().color())
self.tableWidgetBlacklist.item(currentRow, 1).setForeground(
QtWidgets.QApplication.palette().text().color())
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sqlExecute(
'''UPDATE blacklist SET enabled=1 WHERE address=?''',
str(addressAtCurrentRow))
dbstr(addressAtCurrentRow))
else:
sqlExecute(
'''UPDATE whitelist SET enabled=1 WHERE address=?''',
str(addressAtCurrentRow))
dbstr(addressAtCurrentRow))
def on_action_BlacklistDisable(self):
currentRow = self.tableWidgetBlacklist.currentRow()
addressAtCurrentRow = self.tableWidgetBlacklist.item(
currentRow, 1).text()
self.tableWidgetBlacklist.item(
currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128))
self.tableWidgetBlacklist.item(
currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128))
self.tableWidgetBlacklist.item(currentRow, 0).setForeground(
QtGui.QColor(128, 128, 128))
self.tableWidgetBlacklist.item(currentRow, 1).setForeground(
QtGui.QColor(128, 128, 128))
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sqlExecute(
'''UPDATE blacklist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow))
'''UPDATE blacklist SET enabled=0 WHERE address=?''', dbstr(addressAtCurrentRow))
else:
sqlExecute(
'''UPDATE whitelist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow))
'''UPDATE whitelist SET enabled=0 WHERE address=?''', dbstr(addressAtCurrentRow))
def on_action_BlacklistSetAvatar(self):
self.window().on_action_SetAvatar(self.tableWidgetBlacklist)

View File

@ -62,6 +62,9 @@
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
@ -98,11 +101,8 @@
<customwidget>
<class>STableWidget</class>
<extends>QTableWidget</extends>
<header>bitmessageqt/settingsmixin.h</header>
<header>bitmessageqt.settingsmixin</header>
</customwidget>
</customwidgets>
<resources>
<include location="bitmessage_icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -1,22 +1,23 @@
"""
Custom dialog classes
All dialogs are available in this module.
"""
# pylint: disable=too-few-public-methods
from PyQt4 import QtGui
from unqstr import ustr
from qtpy import QtWidgets
import paths
import widgets
from address_dialogs import (
from bitmessageqt import widgets
from .address_dialogs import (
AddAddressDialog, EmailGatewayDialog, NewAddressDialog,
NewSubscriptionDialog, RegenerateAddressesDialog,
SpecialAddressBehaviorDialog
)
from newchandialog import NewChanDialog
from settings import SettingsDialog
from .newchandialog import NewChanDialog
from .settings import SettingsDialog
from tr import _translate
from version import softwareVersion
__all__ = [
"NewChanDialog", "AddAddressDialog", "NewAddressDialog",
"NewSubscriptionDialog", "RegenerateAddressesDialog",
@ -25,8 +26,8 @@ __all__ = [
]
class AboutDialog(QtGui.QDialog):
"""The `About` dialog"""
class AboutDialog(QtWidgets.QDialog):
"""The "About" dialog"""
def __init__(self, parent=None):
super(AboutDialog, self).__init__(parent)
widgets.load('about.ui', self)
@ -36,7 +37,7 @@ class AboutDialog(QtGui.QDialog):
if commit:
version += '-' + commit[:7]
self.labelVersion.setText(
self.labelVersion.text().replace(
ustr(self.labelVersion.text()).replace(
':version:', version
).replace(':branch:', commit or 'v%s' % version)
)
@ -44,17 +45,17 @@ class AboutDialog(QtGui.QDialog):
try:
self.label_2.setText(
self.label_2.text().replace(
'2022', str(last_commit.get('time').year)
ustr(self.label_2.text()).replace(
'2022', ustr(last_commit.get('time').year)
))
except AttributeError:
pass
self.setFixedSize(QtGui.QWidget.sizeHint(self))
self.setFixedSize(QtWidgets.QWidget.sizeHint(self))
class IconGlossaryDialog(QtGui.QDialog):
"""The `Icon Glossary` dialog, explaining the status icon colors"""
class IconGlossaryDialog(QtWidgets.QDialog):
"""The "Icon Glossary" dialog, explaining the status icon colors"""
def __init__(self, parent=None, config=None):
super(IconGlossaryDialog, self).__init__(parent)
widgets.load('iconglossary.ui', self)
@ -64,22 +65,23 @@ class IconGlossaryDialog(QtGui.QDialog):
self.labelPortNumber.setText(_translate(
"iconGlossaryDialog",
"You are using TCP port %1. (This can be changed in the settings)."
).arg(config.getint('bitmessagesettings', 'port')))
self.setFixedSize(QtGui.QWidget.sizeHint(self))
"You are using TCP port {0}."
" (This can be changed in the settings)."
).format(config.getint('bitmessagesettings', 'port')))
self.setFixedSize(QtWidgets.QWidget.sizeHint(self))
class HelpDialog(QtGui.QDialog):
"""The `Help` dialog"""
class HelpDialog(QtWidgets.QDialog):
"""The "Help" dialog"""
def __init__(self, parent=None):
super(HelpDialog, self).__init__(parent)
widgets.load('help.ui', self)
self.setFixedSize(QtGui.QWidget.sizeHint(self))
self.setFixedSize(QtWidgets.QWidget.sizeHint(self))
class ConnectDialog(QtGui.QDialog):
"""The `Connect` dialog"""
class ConnectDialog(QtWidgets.QDialog):
"""The "Connect" dialog"""
def __init__(self, parent=None):
super(ConnectDialog, self).__init__(parent)
widgets.load('connect.ui', self)
self.setFixedSize(QtGui.QWidget.sizeHint(self))
self.setFixedSize(QtWidgets.QWidget.sizeHint(self))

View File

@ -1,18 +1,23 @@
"""
Folder tree and messagelist widgets definitions.
"""
# pylint: disable=too-many-arguments,bad-super-call
# pylint: disable=too-many-arguments
# pylint: disable=attribute-defined-outside-init
from cgi import escape
try:
from cgi import escape
except ImportError:
from html import escape
from PyQt4 import QtCore, QtGui
from unqstr import ustr, unic
from dbcompat import dbstr
from qtpy import QtCore, QtGui, QtWidgets
from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery
from settingsmixin import SettingsMixin
from .settingsmixin import SettingsMixin
from tr import _translate
from utils import avatarize
from .utils import avatarize
# for pylupdate
_translate("MainWindow", "inbox")
@ -38,15 +43,16 @@ class AccountMixin(object):
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]:
elif self.type in (self.MAILINGLIST, self.SUBSCRIPTION):
return QtGui.QColor(137, 4, 177)
return QtGui.QApplication.palette().text().color()
return QtWidgets.QApplication.palette().text().color()
def folderColor(self):
"""QT UI color for a folder"""
if not self.parent().isEnabled:
return QtGui.QColor(128, 128, 128)
return QtGui.QApplication.palette().text().color()
return QtWidgets.QApplication.palette().text().color()
def accountBrush(self):
"""Account brush (for QT UI)"""
@ -62,7 +68,7 @@ class AccountMixin(object):
def accountString(self):
"""Account string suitable for use in To: field: label <address>"""
label = self._getLabel()
label = ustr(self._getLabel())
return (
self.address if label == self.address
else '%s <%s>' % (label, self.address)
@ -73,7 +79,7 @@ class AccountMixin(object):
if address is None:
self.address = None
else:
self.address = str(address)
self.address = ustr(address)
def setUnreadCount(self, cnt):
"""Set number of unread messages"""
@ -83,7 +89,7 @@ class AccountMixin(object):
except AttributeError:
pass
self.unreadCount = int(cnt)
if isinstance(self, QtGui.QTreeWidgetItem):
if isinstance(self, QtWidgets.QTreeWidgetItem):
self.emitDataChanged()
def setEnabled(self, enabled):
@ -97,7 +103,7 @@ class AccountMixin(object):
for i in range(self.childCount()):
if isinstance(self.child(i), Ui_FolderWidget):
self.child(i).setEnabled(enabled)
if isinstance(self, QtGui.QTreeWidgetItem):
if isinstance(self, QtWidgets.QTreeWidgetItem):
self.emitDataChanged()
def setType(self):
@ -111,53 +117,53 @@ class AccountMixin(object):
elif config.safeGetBoolean(self.address, 'mailinglist'):
self.type = self.MAILINGLIST
elif sqlQuery(
'''select label from subscriptions where address=?''', self.address):
'SELECT label FROM subscriptions WHERE address=?',
dbstr(self.address)
):
self.type = AccountMixin.SUBSCRIPTION
else:
self.type = self.NORMAL
def defaultLabel(self):
"""Default label (in case no label is set manually)"""
queryreturn = None
retval = None
queryreturn = retval = None
if self.type in (
AccountMixin.NORMAL,
AccountMixin.CHAN, AccountMixin.MAILINGLIST):
try:
retval = unicode(
config.get(self.address, 'label'), 'utf-8')
retval = unic(ustr(config.get(self.address, 'label')))
except Exception:
queryreturn = sqlQuery(
'''select label from addressbook where address=?''', self.address)
'SELECT label FROM addressbook WHERE address=?',
dbstr(self.address)
)
elif self.type == AccountMixin.SUBSCRIPTION:
queryreturn = sqlQuery(
'''select label from subscriptions where address=?''', self.address)
if queryreturn is not None:
if queryreturn != []:
for row in queryreturn:
retval, = row
retval = unicode(retval, 'utf-8')
'SELECT label FROM subscriptions WHERE address=?',
dbstr(self.address)
)
if queryreturn:
retval = unic(ustr(queryreturn[-1][0]))
elif self.address is None or self.type == AccountMixin.ALL:
return unicode(
str(_translate("MainWindow", "All accounts")), 'utf-8')
return unic(_translate("MainWindow", "All accounts"))
return retval or unicode(self.address, 'utf-8')
return retval or unic(self.address)
class BMTreeWidgetItem(QtGui.QTreeWidgetItem, AccountMixin):
class BMTreeWidgetItem(QtWidgets.QTreeWidgetItem, AccountMixin):
"""A common abstract class for Tree widget item"""
def __init__(self, parent, pos, address, unreadCount):
super(QtGui.QTreeWidgetItem, self).__init__()
super(QtWidgets.QTreeWidgetItem, self).__init__()
self.setAddress(address)
self.setUnreadCount(unreadCount)
self._setup(parent, pos)
def _getAddressBracket(self, unreadCount=False):
return " (" + str(self.unreadCount) + ")" if unreadCount else ""
return " (" + ustr(self.unreadCount) + ")" if unreadCount else ""
def data(self, column, role):
"""Override internal QT method for returning object data"""
"""Override internal Qt method for returning object data"""
if column == 0:
if role == QtCore.Qt.DisplayRole:
return self._getLabel() + self._getAddressBracket(
@ -190,11 +196,11 @@ class Ui_FolderWidget(BMTreeWidgetItem):
return _translate("MainWindow", self.folderName)
def setFolderName(self, fname):
"""Set folder name (for QT UI)"""
self.folderName = str(fname)
"""Set folder name (for Qt UI)"""
self.folderName = ustr(fname)
def data(self, column, role):
"""Override internal QT method for returning object data"""
"""Override internal Qt method for returning object data"""
if column == 0 and role == QtCore.Qt.ForegroundRole:
return self.folderBrush()
return super(Ui_FolderWidget, self).data(column, role)
@ -216,12 +222,14 @@ class Ui_FolderWidget(BMTreeWidgetItem):
return self.folderName < other.folderName
return x >= y if reverse else x < y
return super(QtGui.QTreeWidgetItem, self).__lt__(other)
return super(QtWidgets.QTreeWidgetItem, self).__lt__(other)
class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
"""Item in the account/folder tree representing an account"""
def __init__(self, parent, pos=0, address=None, unreadCount=0, enabled=True):
def __init__(
self, parent, pos=0, address=None, unreadCount=0, enabled=True
):
super(Ui_AddressWidget, self).__init__(
parent, pos, address, unreadCount)
self.setEnabled(enabled)
@ -232,15 +240,13 @@ class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
def _getLabel(self):
if self.address is None:
return unicode(_translate(
"MainWindow", "All accounts").toUtf8(), 'utf-8', 'ignore')
return unic(_translate("MainWindow", "All accounts"))
else:
try:
return unicode(
config.get(self.address, 'label'),
'utf-8', 'ignore')
return unic(ustr(
config.get(self.address, 'label')))
except:
return unicode(self.address, 'utf-8')
return unic(self.address)
def _getAddressBracket(self, unreadCount=False):
ret = "" if self.isExpanded() \
@ -260,15 +266,13 @@ class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
return super(Ui_AddressWidget, self).data(column, role)
def setData(self, column, role, value):
"""Save account label (if you edit in the the UI, this will be triggered and will save it to keys.dat)"""
"""
Save account label (if you edit in the the UI, this will be
triggered and will save it to keys.dat)
"""
if role == QtCore.Qt.EditRole \
and self.type != AccountMixin.SUBSCRIPTION:
config.set(
str(self.address), 'label',
str(value.toString().toUtf8())
if isinstance(value, QtCore.QVariant)
else value.encode('utf-8')
)
config.set(self.address, 'label', ustr(value))
config.save()
return super(Ui_AddressWidget, self).setData(column, role, value)
@ -295,24 +299,26 @@ class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
if self._getSortRank() < other._getSortRank() else reverse
)
return super(QtGui.QTreeWidgetItem, self).__lt__(other)
return super(Ui_AddressWidget, self).__lt__(other)
class Ui_SubscriptionWidget(Ui_AddressWidget):
"""Special treating of subscription addresses"""
# pylint: disable=unused-argument
def __init__(self, parent, pos=0, address="", unreadCount=0, label="", enabled=True):
def __init__(
self, parent, pos=0, address="", unreadCount=0, label="",
enabled=True
):
super(Ui_SubscriptionWidget, self).__init__(
parent, pos, address, unreadCount, enabled)
def _getLabel(self):
queryreturn = sqlQuery(
'''select label from subscriptions where address=?''', self.address)
if queryreturn != []:
for row in queryreturn:
retval, = row
return unicode(retval, 'utf-8', 'ignore')
return unicode(self.address, 'utf-8')
'SELECT label FROM subscriptions WHERE address=?',
dbstr(self.address))
if queryreturn:
return unic(ustr(queryreturn[-1][0]))
return unic(self.address)
def setType(self):
"""Set account type"""
@ -322,22 +328,17 @@ class Ui_SubscriptionWidget(Ui_AddressWidget):
def setData(self, column, role, value):
"""Save subscription label to database"""
if role == QtCore.Qt.EditRole:
if isinstance(value, QtCore.QVariant):
label = str(
value.toString().toUtf8()).decode('utf-8', 'ignore')
else:
label = unicode(value, 'utf-8', 'ignore')
sqlExecute(
'''UPDATE subscriptions SET label=? WHERE address=?''',
label, self.address)
'UPDATE subscriptions SET label=? WHERE address=?',
dbstr(unic(ustr(value))), dbstr(self.address))
return super(Ui_SubscriptionWidget, self).setData(column, role, value)
class BMTableWidgetItem(QtGui.QTableWidgetItem, SettingsMixin):
class BMTableWidgetItem(QtWidgets.QTableWidgetItem, SettingsMixin):
"""A common abstract class for Table widget item"""
def __init__(self, label=None, unread=False):
super(QtGui.QTableWidgetItem, self).__init__()
super(QtWidgets.QTableWidgetItem, self).__init__()
self.setLabel(label)
self.setUnread(unread)
self._setup()
@ -407,18 +408,17 @@ class MessageList_AddressWidget(BMAddressWidget):
AccountMixin.NORMAL,
AccountMixin.CHAN, AccountMixin.MAILINGLIST):
try:
newLabel = unicode(
config.get(self.address, 'label'),
'utf-8', 'ignore')
newLabel = unic(ustr(config.get(self.address, 'label')))
except:
queryreturn = sqlQuery(
'''select label from addressbook where address=?''', self.address)
'SELECT label FROM addressbook WHERE address=?',
dbstr(self.address))
elif self.type == AccountMixin.SUBSCRIPTION:
queryreturn = sqlQuery(
'''select label from subscriptions where address=?''', self.address)
'SELECT label FROM subscriptions WHERE address=?',
dbstr(self.address))
if queryreturn:
for row in queryreturn:
newLabel = unicode(row[0], 'utf-8', 'ignore')
newLabel = unic(ustr(queryreturn[-1][0]))
self.label = newLabel
@ -438,7 +438,7 @@ class MessageList_AddressWidget(BMAddressWidget):
def __lt__(self, other):
if isinstance(other, MessageList_AddressWidget):
return self.label.lower() < other.label.lower()
return super(QtGui.QTableWidgetItem, self).__lt__(other)
return super(MessageList_AddressWidget, self).__lt__(other)
class MessageList_SubjectWidget(BMTableWidgetItem):
@ -454,16 +454,16 @@ class MessageList_SubjectWidget(BMTableWidgetItem):
def data(self, role):
"""Return object data (QT UI)"""
if role == QtCore.Qt.UserRole:
return self.subject
return ustr(self.subject)
if role == QtCore.Qt.ToolTipRole:
return escape(unicode(self.subject, 'utf-8'))
return escape(unic(ustr(self.subject)))
return super(MessageList_SubjectWidget, self).data(role)
# label (or address) alphabetically, disabled at the end
def __lt__(self, other):
if isinstance(other, MessageList_SubjectWidget):
return self.label.lower() < other.label.lower()
return super(QtGui.QTableWidgetItem, self).__lt__(other)
return super(MessageList_SubjectWidget, self).__lt__(other)
# In order for the time columns on the Inbox and Sent tabs to be sorted
@ -476,9 +476,9 @@ class MessageList_TimeWidget(BMTableWidgetItem):
msgid is available by QtCore.Qt.UserRole
"""
def __init__(self, label=None, unread=False, timestamp=None, msgid=''):
def __init__(self, label=None, unread=False, timestamp=None, msgid=b''):
super(MessageList_TimeWidget, self).__init__(label, unread)
self.setData(QtCore.Qt.UserRole, QtCore.QByteArray(msgid))
self.setData(QtCore.Qt.UserRole, QtCore.QByteArray(bytes(msgid)))
self.setData(TimestampRole, int(timestamp))
def __lt__(self, other):
@ -491,15 +491,14 @@ class MessageList_TimeWidget(BMTableWidgetItem):
"""
data = super(MessageList_TimeWidget, self).data(role)
if role == TimestampRole:
return int(data.toPyObject())
return int(data)
if role == QtCore.Qt.UserRole:
return str(data.toPyObject())
return ustr(data)
return data
class Ui_AddressBookWidgetItem(BMAddressWidget):
"""Addressbook item"""
# pylint: disable=unused-argument
def __init__(self, label=None, acc_type=AccountMixin.NORMAL):
self.type = acc_type
super(Ui_AddressBookWidgetItem, self).__init__(label=label)
@ -513,10 +512,7 @@ class Ui_AddressBookWidgetItem(BMAddressWidget):
def setData(self, role, value):
"""Set data"""
if role == QtCore.Qt.EditRole:
self.label = str(
value.toString().toUtf8()
if isinstance(value, QtCore.QVariant) else value
)
self.label = ustr(value)
if self.type in (
AccountMixin.NORMAL,
AccountMixin.MAILINGLIST, AccountMixin.CHAN):
@ -525,28 +521,33 @@ class Ui_AddressBookWidgetItem(BMAddressWidget):
config.set(self.address, 'label', self.label)
config.save()
except:
sqlExecute('''UPDATE addressbook set label=? WHERE address=?''', self.label, self.address)
sqlExecute(
'UPDATE addressbook SET label=? WHERE address=?',
dbstr(self.label), dbstr(self.address)
)
elif self.type == AccountMixin.SUBSCRIPTION:
sqlExecute('''UPDATE subscriptions set label=? WHERE address=?''', self.label, self.address)
else:
pass
sqlExecute(
'UPDATE subscriptions SET label=? WHERE address=?',
dbstr(self.label), dbstr(self.address))
return super(Ui_AddressBookWidgetItem, self).setData(role, value)
def __lt__(self, other):
if isinstance(other, Ui_AddressBookWidgetItem):
if not isinstance(other, Ui_AddressBookWidgetItem):
return super(Ui_AddressBookWidgetItem, self).__lt__(other)
reverse = QtCore.Qt.DescendingOrder == \
self.tableWidget().horizontalHeader().sortIndicatorOrder()
if self.type == other.type:
return self.label.lower() < other.label.lower()
return not reverse if self.type < other.type else reverse
return super(QtGui.QTableWidgetItem, self).__lt__(other)
class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem):
"""Addressbook label item"""
def __init__(self, address, label, acc_type):
self.address = address
self.address = ustr(address)
super(Ui_AddressBookWidgetItemLabel, self).__init__(label, acc_type)
def data(self, role):
@ -558,7 +559,7 @@ class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem):
class Ui_AddressBookWidgetItemAddress(Ui_AddressBookWidgetItem):
"""Addressbook address item"""
def __init__(self, address, label, acc_type):
self.address = address
self.address = ustr(address)
super(Ui_AddressBookWidgetItemAddress, self).__init__(address, acc_type)
def data(self, role):
@ -570,28 +571,27 @@ class Ui_AddressBookWidgetItemAddress(Ui_AddressBookWidgetItem):
return super(Ui_AddressBookWidgetItemAddress, self).data(role)
class AddressBookCompleter(QtGui.QCompleter):
class AddressBookCompleter(QtWidgets.QCompleter):
"""Addressbook completer"""
def __init__(self):
super(AddressBookCompleter, self).__init__()
self.cursorPos = -1
def onCursorPositionChanged(self, oldPos, newPos): # pylint: disable=unused-argument
def onCursorPositionChanged(self, oldPos, newPos):
"""Callback for cursor position change"""
# pylint: disable=unused-argument
if oldPos != self.cursorPos:
self.cursorPos = -1
def splitPath(self, path):
"""Split on semicolon"""
text = unicode(path.toUtf8(), 'utf-8')
text = unic(ustr(path))
return [text[:self.widget().cursorPosition()].split(';')[-1].strip()]
def pathFromIndex(self, index):
"""Perform autocompletion (reimplemented QCompleter method)"""
autoString = unicode(
index.data(QtCore.Qt.EditRole).toString().toUtf8(), 'utf-8')
text = unicode(self.widget().text().toUtf8(), 'utf-8')
autoString = unic(ustr(index.data(QtCore.Qt.EditRole).toString()))
text = unic(ustr(self.widget().text()))
# If cursor position was saved, restore it, else save it
if self.cursorPos != -1:
@ -620,7 +620,6 @@ class AddressBookCompleter(QtGui.QCompleter):
# Get string value from before auto finished string is selected
# pre = text[prevDelimiterIndex + 1:curIndex - 1]
# Get part of string that occurs AFTER cursor
part2 = text[nextDelimiterIndex:]

View File

@ -1,48 +1,56 @@
"""Language Box Module for Locale Settings"""
# pylint: disable=too-few-public-methods,bad-continuation
"""LanguageBox widget is for selecting UI language"""
import glob
import os
from PyQt4 import QtCore, QtGui
from qtpy import QtCore, QtWidgets
import paths
from bmconfigparser import config
from tr import _translate
class LanguageBox(QtGui.QComboBox):
"""LanguageBox class for Qt UI"""
# pylint: disable=too-few-public-methods
class LanguageBox(QtWidgets.QComboBox):
"""A subclass of `QtWidgets.QComboBox` for selecting language"""
languageName = {
"system": "System Settings", "eo": "Esperanto",
"system": "System Settings",
"eo": "Esperanto",
"en_pirate": "Pirate English"
}
def __init__(self, parent=None):
super(QtGui.QComboBox, self).__init__(parent)
super(LanguageBox, self).__init__(parent)
self.populate()
def populate(self):
"""Populates drop down list with all available languages."""
self.clear()
localesPath = os.path.join(paths.codePath(), 'translations')
self.addItem(QtGui.QApplication.translate(
"settingsDialog", "System Settings", "system"), "system")
self.addItem(
_translate("settingsDialog", "System Settings", "system"),
"system"
)
self.setCurrentIndex(0)
self.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically)
self.setInsertPolicy(QtWidgets.QComboBox.InsertAlphabetically)
for translationFile in sorted(
glob.glob(os.path.join(localesPath, "bitmessage_*.qm"))
):
localeShort = \
os.path.split(translationFile)[1].split("_", 1)[1][:-3]
locale = QtCore.QLocale(localeShort)
if localeShort in LanguageBox.languageName:
self.addItem(
LanguageBox.languageName[localeShort], localeShort)
elif locale.nativeLanguageName() == "":
self.addItem(localeShort, localeShort)
else:
locale = QtCore.QLocale(localeShort)
self.addItem(
locale.nativeLanguageName() or localeShort, localeShort)
configuredLocale = config.safeGet(
'bitmessagesettings', 'userlocale', "system")
'bitmessagesettings', 'userlocale', 'system')
for i in range(self.count()):
if self.itemData(i) == configuredLocale:
self.setCurrentIndex(i)

View File

@ -1,33 +1,34 @@
"""
Message editor with a wheel zoom functionality
"""
# pylint: disable=bad-continuation
"""The MessageCompose class definition"""
from PyQt4 import QtCore, QtGui
from qtpy import QtCore, QtWidgets
from tr import _translate
class MessageCompose(QtGui.QTextEdit):
class MessageCompose(QtWidgets.QTextEdit):
"""Editor class with wheel zoom functionality"""
def __init__(self, parent=0):
def __init__(self, parent=None):
super(MessageCompose, self).__init__(parent)
# we'll deal with this later when we have a new message format
self.setAcceptRichText(False)
self.defaultFontPointSize = self.currentFont().pointSize()
def wheelEvent(self, event):
"""Mouse wheel scroll event handler"""
if (
QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ControlModifier
) == QtCore.Qt.ControlModifier and event.orientation() == QtCore.Qt.Vertical:
(QtWidgets.QApplication.queryKeyboardModifiers()
& QtCore.Qt.ControlModifier) == QtCore.Qt.ControlModifier
and event.angleDelta().y() != 0
):
if event.delta() > 0:
self.zoomIn(1)
else:
self.zoomOut(1)
zoom = self.currentFont().pointSize() * 100 / self.defaultFontPointSize
QtGui.QApplication.activeWindow().statusBar().showMessage(
QtGui.QApplication.translate("MainWindow", "Zoom level %1%").arg(
str(zoom)
)
)
QtWidgets.QApplication.activeWindow().statusbar.showMessage(
_translate("MainWindow", "Zoom level {0}%").format(
# zoom percentage
self.currentFont().pointSize() * 100
/ self.defaultFontPointSize
))
else:
# in QTextEdit, super does not zoom, only scroll
super(MessageCompose, self).wheelEvent(event)

View File

@ -1,22 +1,22 @@
"""
Custom message viewer with support for switching between HTML and plain
text rendering, HTML sanitization, lazy rendering (as you scroll down),
zoom and URL click warning popup
zoom and URL click warning popup.
"""
from PyQt4 import QtCore, QtGui
from unqstr import ustr, unic
from qtpy import QtCore, QtGui, QtWidgets
from safehtmlparser import SafeHTMLParser
from .safehtmlparser import SafeHTMLParser
from tr import _translate
class MessageView(QtGui.QTextBrowser):
class MessageView(QtWidgets.QTextBrowser):
"""Message content viewer class, can switch between plaintext and HTML"""
MODE_PLAIN = 0
MODE_HTML = 1
def __init__(self, parent=0):
def __init__(self, parent=None):
super(MessageView, self).__init__(parent)
self.mode = MessageView.MODE_PLAIN
self.html = None
@ -38,8 +38,11 @@ class MessageView(QtGui.QTextBrowser):
def mousePressEvent(self, event):
"""Mouse press button event handler"""
if event.button() == QtCore.Qt.LeftButton and self.html and self.html.has_html and self.cursorForPosition(
event.pos()).block().blockNumber() == 0:
if (
event.button() == QtCore.Qt.LeftButton
and self.html and self.html.has_html
and self.cursorForPosition(event.pos()).block().blockNumber() == 0
):
if self.mode == MessageView.MODE_PLAIN:
self.showHTML()
else:
@ -52,23 +55,23 @@ class MessageView(QtGui.QTextBrowser):
# super will actually automatically take care of zooming
super(MessageView, self).wheelEvent(event)
if (
QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ControlModifier
) == QtCore.Qt.ControlModifier and event.orientation() == QtCore.Qt.Vertical:
(QtWidgets.QApplication.queryKeyboardModifiers()
& QtCore.Qt.ControlModifier) == QtCore.Qt.ControlModifier
and event.angleDelta().y() != 0
):
zoom = self.currentFont().pointSize() * 100 / self.defaultFontPointSize
QtGui.QApplication.activeWindow().statusBar().showMessage(_translate(
"MainWindow", "Zoom level %1%").arg(str(zoom)))
QtWidgets.QApplication.activeWindow().statusbar.showMessage(
_translate("MainWindow", "Zoom level {0}%").format(zoom))
def setWrappingWidth(self, width=None):
"""Set word-wrapping width"""
self.setLineWrapMode(QtGui.QTextEdit.FixedPixelWidth)
if width is None:
width = self.width()
self.setLineWrapColumnOrWidth(width)
self.setLineWrapMode(QtWidgets.QTextEdit.FixedPixelWidth)
self.setLineWrapColumnOrWidth(width or self.width())
def confirmURL(self, link):
"""Show a dialog requesting URL opening confirmation"""
if link.scheme() == "mailto":
window = QtGui.QApplication.activeWindow()
window = QtWidgets.QApplication.activeWindow()
window.ui.lineEditTo.setText(link.path())
if link.hasQueryItem("subject"):
window.ui.lineEditSubject.setText(
@ -83,39 +86,40 @@ class MessageView(QtGui.QTextBrowser):
)
window.ui.textEditMessage.setFocus()
return
reply = QtGui.QMessageBox.warning(
self,
QtGui.QApplication.translate(
reply = QtWidgets.QMessageBox.warning(
self, _translate("MessageView", "Follow external link"),
_translate(
"MessageView",
"Follow external link"),
QtGui.QApplication.translate(
"MessageView",
"The link \"%1\" will open in a browser. It may be a security risk, it could de-anonymise you"
" or download malicious data. Are you sure?").arg(unicode(link.toString())),
QtGui.QMessageBox.Yes,
QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
"The link \"{0}\" will open in a browser. It may be"
" a security risk, it could de-anonymise you or download"
" malicious data. Are you sure?"
).format(unic(ustr(link.toString()))),
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
QtGui.QDesktopServices.openUrl(link)
def loadResource(self, restype, name):
"""
Callback for loading referenced objects, such as an image. For security reasons at the moment doesn't do
anything)
Callback for loading referenced objects, such as an image.
For security reasons at the moment doesn't do anything
"""
pass
def lazyRender(self):
"""
Partially render a message. This is to avoid UI freezing when loading huge messages. It continues loading as
you scroll down.
Partially render a message. This is to avoid UI freezing when
loading huge messages. It continues loading as you scroll down.
"""
if self.rendering:
return
self.rendering = True
position = self.verticalScrollBar().value()
cursor = QtGui.QTextCursor(self.document())
while self.outpos < len(self.out) and self.verticalScrollBar().value(
) >= self.document().size().height() - 2 * self.size().height():
while (
self.outpos < len(self.out)
and self.verticalScrollBar().value()
>= self.document().size().height() - 2 * self.size().height()
):
startpos = self.outpos
self.outpos += 10240
# find next end of tag
@ -123,8 +127,9 @@ class MessageView(QtGui.QTextBrowser):
pos = self.out.find(">", self.outpos)
if pos > self.outpos:
self.outpos = pos + 1
cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor)
cursor.insertHtml(QtCore.QString(self.out[startpos:self.outpos]))
cursor.movePosition(
QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor)
cursor.insertHtml(unic(self.out[startpos:self.outpos]))
self.verticalScrollBar().setValue(position)
self.rendering = False
@ -133,9 +138,11 @@ class MessageView(QtGui.QTextBrowser):
self.mode = MessageView.MODE_PLAIN
out = self.html.raw
if self.html.has_html:
out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unicode(
QtGui.QApplication.translate(
"MessageView", "HTML detected, click here to display")) + "</b></div><br/>" + out
out = (
'<div align="center" style="text-decoration: underline;"><b>'
+ unic(ustr(_translate(
"MessageView", "HTML detected, click here to display"
))) + '</b></div><br/>' + out)
self.out = out
self.outpos = 0
self.setHtml("")
@ -144,10 +151,10 @@ class MessageView(QtGui.QTextBrowser):
def showHTML(self):
"""Render message as HTML"""
self.mode = MessageView.MODE_HTML
out = self.html.sanitised
out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unicode(
QtGui.QApplication.translate("MessageView", "Click here to disable HTML")) + "</b></div><br/>" + out
self.out = out
self.out = (
'<div align="center" style="text-decoration: underline;"><b>'
+ _translate("MessageView", "Click here to disable HTML")
+ '</b></div><br/>' + self.html.sanitised)
self.outpos = 0
self.setHtml("")
self.lazyRender()
@ -155,8 +162,6 @@ class MessageView(QtGui.QTextBrowser):
def setContent(self, data):
"""Set message content from argument"""
self.html = SafeHTMLParser()
self.html.reset()
self.html.reset_safe()
self.html.allow_picture = True
self.html.feed(data)
self.html.close()

View File

@ -1,16 +1,15 @@
#!/usr/bin/env python2.7
from PyQt4 import QtCore, QtGui
from qtpy import QtCore, QtWidgets
class MigrationWizardIntroPage(QtGui.QWizardPage):
class MigrationWizardIntroPage(QtWidgets.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
super(QtWidgets.QWizardPage, self).__init__()
self.setTitle("Migrating configuration")
label = QtGui.QLabel("This wizard will help you to migrate your configuration. "
label = QtWidgets.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 = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
@ -18,15 +17,15 @@ class MigrationWizardIntroPage(QtGui.QWizardPage):
return 1
class MigrationWizardAddressesPage(QtGui.QWizardPage):
class MigrationWizardAddressesPage(QtWidgets.QWizardPage):
def __init__(self, addresses):
super(QtGui.QWizardPage, self).__init__()
super(QtWidgets.QWizardPage, self).__init__()
self.setTitle("Addresses")
label = QtGui.QLabel("Please select addresses that you are already using with mailchuck. ")
label = QtWidgets.QLabel("Please select addresses that you are already using with mailchuck. ")
label.setWordWrap(True)
layout = QtGui.QVBoxLayout()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
@ -34,15 +33,15 @@ class MigrationWizardAddressesPage(QtGui.QWizardPage):
return 10
class MigrationWizardGPUPage(QtGui.QWizardPage):
class MigrationWizardGPUPage(QtWidgets.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
super(QtWidgets.QWizardPage, self).__init__()
self.setTitle("GPU")
label = QtGui.QLabel("Are you using a GPU? ")
label = QtWidgets.QLabel("Are you using a GPU? ")
label.setWordWrap(True)
layout = QtGui.QVBoxLayout()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
@ -50,22 +49,22 @@ class MigrationWizardGPUPage(QtGui.QWizardPage):
return 10
class MigrationWizardConclusionPage(QtGui.QWizardPage):
class MigrationWizardConclusionPage(QtWidgets.QWizardPage):
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
super(QtWidgets.QWizardPage, self).__init__()
self.setTitle("All done!")
label = QtGui.QLabel("You successfully migrated.")
label = QtWidgets.QLabel("You successfully migrated.")
label.setWordWrap(True)
layout = QtGui.QVBoxLayout()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
class Ui_MigrationWizard(QtGui.QWizard):
class Ui_MigrationWizard(QtWidgets.QWizard):
def __init__(self, addresses):
super(QtGui.QWizard, self).__init__()
super(QtWidgets.QWizard, self).__init__()
self.pages = {}

View File

@ -4,26 +4,28 @@ Network status tab widget definition.
import time
from PyQt4 import QtCore, QtGui
from qtpy import QtCore, QtGui, QtWidgets
import l10n
import network.stats
import state
import widgets
from bitmessageqt import widgets
from network import connectionpool, knownnodes
from retranslateui import RetranslateMixin
from .retranslateui import RetranslateMixin
from tr import _translate
from uisignaler import UISignaler
from .uisignaler import UISignaler
class NetworkStatus(QtGui.QWidget, RetranslateMixin):
class NetworkStatus(QtWidgets.QWidget, RetranslateMixin):
"""Network status tab"""
def __init__(self, parent=None):
super(NetworkStatus, self).__init__(parent)
widgets.load('networkstatus.ui', self)
header = self.tableWidgetConnectionCount.horizontalHeader()
header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
# Somehow this value was 5 when I tested
if header.sortIndicatorSection() > 4:
@ -32,20 +34,17 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
self.startup = time.localtime()
self.UISignalThread = UISignaler.get()
# pylint: disable=no-member
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateNumberOfMessagesProcessed()"), self.updateNumberOfMessagesProcessed)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateNumberOfPubkeysProcessed()"), self.updateNumberOfPubkeysProcessed)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateNumberOfBroadcastsProcessed()"), self.updateNumberOfBroadcastsProcessed)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.updateNetworkStatusTab)
self.UISignalThread.updateNumberOfMessagesProcessed.connect(
self.updateNumberOfMessagesProcessed)
self.UISignalThread.updateNumberOfPubkeysProcessed.connect(
self.updateNumberOfPubkeysProcessed)
self.UISignalThread.updateNumberOfBroadcastsProcessed.connect(
self.updateNumberOfBroadcastsProcessed)
self.UISignalThread.updateNetworkStatusTab.connect(
self.updateNetworkStatusTab)
self.timer = QtCore.QTimer()
QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds)
# pylint: enable=no-member
self.timer.timeout.connect(self.runEveryTwoSeconds)
def startUpdate(self):
"""Start a timer to update counters every 2 seconds"""
@ -57,91 +56,66 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
"""Stop counter update timer"""
self.timer.stop()
def formatBytes(self, num):
@staticmethod
def formatBytes(num):
"""Format bytes nicely (SI prefixes)"""
# pylint: disable=no-self-use
for x in [
_translate(
"networkstatus",
"byte(s)",
None,
QtCore.QCoreApplication.CodecForTr,
num),
"kB",
"MB",
"GB",
]:
for x in (
_translate("networkstatus", "byte(s)", None, num),
"kB", "MB", "GB"
):
if num < 1000.0:
return "%3.0f %s" % (num, x)
num /= 1000.0
return "%3.0f %s" % (num, 'TB')
return "%3.0f %s" % (num, "TB")
def formatByteRate(self, num):
@staticmethod
def formatByteRate(num):
"""Format transfer speed in kB/s"""
# pylint: disable=no-self-use
num /= 1000
return "%4.0f kB" % num
def updateNumberOfObjectsToBeSynced(self):
"""Update the counter for number of objects to be synced"""
self.labelSyncStatus.setText(
_translate(
"networkstatus",
"Object(s) to be synced: %n",
None,
QtCore.QCoreApplication.CodecForTr,
network.stats.pendingDownload()
+ network.stats.pendingUpload()))
self.labelSyncStatus.setText(_translate(
"networkstatus", "Object(s) to be synced: %n", None,
network.stats.pendingDownload() + network.stats.pendingUpload()))
def updateNumberOfMessagesProcessed(self):
"""Update the counter for number of processed messages"""
self.updateNumberOfObjectsToBeSynced()
self.labelMessageCount.setText(
_translate(
"networkstatus",
"Processed %n person-to-person message(s).",
None,
QtCore.QCoreApplication.CodecForTr,
state.numberOfMessagesProcessed))
self.labelMessageCount.setText(_translate(
"networkstatus", "Processed %n person-to-person message(s).",
None, state.numberOfMessagesProcessed))
def updateNumberOfBroadcastsProcessed(self):
"""Update the counter for the number of processed broadcasts"""
self.updateNumberOfObjectsToBeSynced()
self.labelBroadcastCount.setText(
_translate(
"networkstatus",
"Processed %n broadcast message(s).",
None,
QtCore.QCoreApplication.CodecForTr,
self.labelBroadcastCount.setText(_translate(
"networkstatus", "Processed %n broadcast message(s).", None,
state.numberOfBroadcastsProcessed))
def updateNumberOfPubkeysProcessed(self):
"""Update the counter for the number of processed pubkeys"""
self.updateNumberOfObjectsToBeSynced()
self.labelPubkeyCount.setText(
_translate(
"networkstatus",
"Processed %n public key(s).",
None,
QtCore.QCoreApplication.CodecForTr,
self.labelPubkeyCount.setText(_translate(
"networkstatus", "Processed %n public key(s).", None,
state.numberOfPubkeysProcessed))
def updateNumberOfBytes(self):
"""
This function is run every two seconds, so we divide the rate of bytes
sent and received by 2.
This function is run every two seconds, so we divide the rate
of bytes sent and received by 2.
"""
self.labelBytesRecvCount.setText(
_translate(
"networkstatus",
"Down: %1/s Total: %2").arg(
self.labelBytesRecvCount.setText(_translate(
"networkstatus", "Down: {0}/s Total: {1}").format(
self.formatByteRate(network.stats.downloadSpeed()),
self.formatBytes(network.stats.receivedBytes())))
self.labelBytesSentCount.setText(
_translate(
"networkstatus", "Up: %1/s Total: %2").arg(
self.formatBytes(network.stats.receivedBytes())
))
self.labelBytesSentCount.setText(_translate(
"networkstatus", "Up: {0}/s Total: {1}").format(
self.formatByteRate(network.stats.uploadSpeed()),
self.formatBytes(network.stats.sentBytes())))
self.formatBytes(network.stats.sentBytes())
))
def updateNetworkStatusTab(self, outbound, add, destination):
"""Add or remove an entry to the list of connected peers"""
@ -168,67 +142,67 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
if add:
self.tableWidgetConnectionCount.insertRow(0)
self.tableWidgetConnectionCount.setItem(
0, 0,
QtGui.QTableWidgetItem("%s:%i" % (destination.host, destination.port))
)
0, 0, QtWidgets.QTableWidgetItem(
"%s:%i" % (destination.host, destination.port)))
self.tableWidgetConnectionCount.setItem(
0, 2,
QtGui.QTableWidgetItem("%s" % (c.userAgent))
)
0, 2, QtWidgets.QTableWidgetItem("%s" % (c.userAgent.decode("utf-8", "replace"))))
self.tableWidgetConnectionCount.setItem(
0, 3,
QtGui.QTableWidgetItem("%s" % (c.tlsVersion))
)
0, 3, QtWidgets.QTableWidgetItem("%s" % (c.tlsVersion)))
self.tableWidgetConnectionCount.setItem(
0, 4,
QtGui.QTableWidgetItem("%s" % (",".join(map(str, c.streams))))
)
0, 4, QtWidgets.QTableWidgetItem(
"%s" % ",".join(map(str, c.streams))))
try:
# .. todo:: FIXME: hard coded stream no
rating = "%.1f" % (knownnodes.knownNodes[1][destination]['rating'])
rating = "%.1f" % knownnodes.knownNodes[1][destination]['rating']
except KeyError:
rating = "-"
self.tableWidgetConnectionCount.setItem(
0, 1,
QtGui.QTableWidgetItem("%s" % (rating))
)
0, 1, QtWidgets.QTableWidgetItem("%s" % (rating)))
if outbound:
brush = QtGui.QBrush(QtGui.QColor("yellow"), QtCore.Qt.SolidPattern)
brush = QtGui.QBrush(
QtGui.QColor("yellow"), QtCore.Qt.SolidPattern)
else:
brush = QtGui.QBrush(QtGui.QColor("green"), QtCore.Qt.SolidPattern)
brush = QtGui.QBrush(
QtGui.QColor("green"), QtCore.Qt.SolidPattern)
for j in range(1):
self.tableWidgetConnectionCount.item(0, j).setBackground(brush)
self.tableWidgetConnectionCount.item(0, 0).setData(QtCore.Qt.UserRole, destination)
self.tableWidgetConnectionCount.item(0, 1).setData(QtCore.Qt.UserRole, outbound)
self.tableWidgetConnectionCount.item(0, 0).setData(
QtCore.Qt.UserRole, destination)
self.tableWidgetConnectionCount.item(0, 1).setData(
QtCore.Qt.UserRole, outbound)
else:
if not connectionpool.pool.inboundConnections:
self.window().setStatusIcon('yellow')
for i in range(self.tableWidgetConnectionCount.rowCount()):
if self.tableWidgetConnectionCount.item(i, 0).data(QtCore.Qt.UserRole).toPyObject() != destination:
if self.tableWidgetConnectionCount.item(i, 0).data(
QtCore.Qt.UserRole) != destination:
continue
if self.tableWidgetConnectionCount.item(i, 1).data(QtCore.Qt.UserRole).toPyObject() == outbound:
if self.tableWidgetConnectionCount.item(i, 1).data(
QtCore.Qt.UserRole) == outbound:
self.tableWidgetConnectionCount.removeRow(i)
break
self.tableWidgetConnectionCount.setUpdatesEnabled(True)
self.tableWidgetConnectionCount.setSortingEnabled(True)
self.labelTotalConnections.setText(
_translate(
"networkstatus", "Total Connections: %1").arg(
str(self.tableWidgetConnectionCount.rowCount())))
# FYI: The 'singlelistener' thread sets the icon color to green when it
# receives an incoming connection, meaning that the user's firewall is
# configured correctly.
if self.tableWidgetConnectionCount.rowCount() and state.statusIconColor == 'red':
self.labelTotalConnections.setText(_translate(
"networkstatus", "Total Connections: {0}").format(
self.tableWidgetConnectionCount.rowCount()
))
# FYI: The 'singlelistener' thread sets the icon color to green
# when it receives an incoming connection, meaning that the user's
# firewall is configured correctly.
if self.tableWidgetConnectionCount.rowCount():
if state.statusIconColor == 'red':
self.window().setStatusIcon('yellow')
elif self.tableWidgetConnectionCount.rowCount() == 0 and state.statusIconColor != "red":
elif state.statusIconColor != 'red':
self.window().setStatusIcon('red')
# timer driven
def runEveryTwoSeconds(self):
"""Updates counters, runs every 2 seconds if the timer is running"""
self.labelLookupsPerSecond.setText(_translate("networkstatus", "Inventory lookups per second: %1").arg(
str(state.Inventory.numberOfInventoryLookupsPerformed / 2)))
self.labelLookupsPerSecond.setText(_translate(
"networkstatus", "Inventory lookups per second: {0}"
).format(state.Inventory.numberOfInventoryLookupsPerformed / 2))
state.Inventory.numberOfInventoryLookupsPerformed = 0
self.updateNumberOfBytes()
self.updateNumberOfObjectsToBeSynced()
@ -236,13 +210,12 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
def retranslateUi(self):
"""Conventional Qt Designer method for dynamic l10n"""
super(NetworkStatus, self).retranslateUi()
self.labelTotalConnections.setText(
_translate(
"networkstatus", "Total Connections: %1").arg(
str(self.tableWidgetConnectionCount.rowCount())))
self.labelTotalConnections.setText(_translate(
"networkstatus", "Total Connections: {0}"
).format(self.tableWidgetConnectionCount.rowCount()))
self.labelStartupTime.setText(_translate(
"networkstatus", "Since startup on %1"
).arg(l10n.formatTimestamp(self.startup)))
"networkstatus", "Since startup on {0}"
).format(l10n.formatTimestamp(self.startup)))
self.updateNumberOfMessagesProcessed()
self.updateNumberOfBroadcastsProcessed()
self.updateNumberOfPubkeysProcessed()

View File

@ -100,6 +100,9 @@
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
@ -109,9 +112,6 @@
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
@ -296,11 +296,8 @@
<customwidget>
<class>STableWidget</class>
<extends>QTableWidget</extends>
<header>bitmessageqt/settingsmixin.h</header>
<header>bitmessageqt.settingsmixin</header>
</customwidget>
</customwidgets>
<resources>
<include location="bitmessage_icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -375,7 +375,7 @@ The 'Random Number' option is selected by default but deterministic addresses ha
<sender>radioButtonDeterministicAddress</sender>
<signal>toggled(bool)</signal>
<receiver>groupBoxDeterministic</receiver>
<slot>setShown(bool)</slot>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>92</x>
@ -391,7 +391,7 @@ The 'Random Number' option is selected by default but deterministic addresses ha
<sender>radioButtonRandomAddress</sender>
<signal>toggled(bool)</signal>
<receiver>groupBox</receiver>
<slot>setShown(bool)</slot>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>72</x>

View File

@ -1,44 +1,34 @@
"""
src/bitmessageqt/newchandialog.py
=================================
NewChanDialog class definition
"""
from PyQt4 import QtCore, QtGui
from unqstr import ustr, unic
from qtpy import QtCore, QtWidgets
import widgets
from bitmessageqt import widgets
from addresses import addBMIfNotPresent
from addressvalidator import AddressValidator, PassPhraseValidator
from .addressvalidator import AddressValidator, PassPhraseValidator
from queues import (
addressGeneratorQueue, apiAddressGeneratorReturnQueue, UISignalQueue)
from tr import _translate
from utils import str_chan
from .utils import str_chan
class NewChanDialog(QtGui.QDialog):
"""The `New Chan` dialog"""
class NewChanDialog(QtWidgets.QDialog):
"""The "New Chan" dialog"""
def __init__(self, parent=None):
super(NewChanDialog, self).__init__(parent)
widgets.load('newchandialog.ui', self)
self.parent = parent
self.chanAddress.setValidator(
AddressValidator(
self.chanAddress,
self.chanPassPhrase,
self.validatorFeedback,
self.buttonBox,
False))
self.chanPassPhrase.setValidator(
PassPhraseValidator(
self.chanPassPhrase,
self.chanAddress,
self.validatorFeedback,
self.buttonBox,
False))
self.chanAddress.setValidator(AddressValidator(
self.chanAddress, self.chanPassPhrase, self.validatorFeedback,
self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok), False))
self.chanPassPhrase.setValidator(PassPhraseValidator(
self.chanPassPhrase, self.chanAddress, self.validatorFeedback,
self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok), False))
self.timer = QtCore.QTimer()
QtCore.QObject.connect( # pylint: disable=no-member
self.timer, QtCore.SIGNAL("timeout()"), self.delayedUpdateStatus)
self.timer.timeout.connect(self.delayedUpdateStatus)
self.timer.start(500) # milliseconds
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.show()
@ -52,32 +42,38 @@ class NewChanDialog(QtGui.QDialog):
self.timer.stop()
self.hide()
apiAddressGeneratorReturnQueue.queue.clear()
if self.chanAddress.text().toUtf8() == "":
if ustr(self.chanAddress.text()) == "":
addressGeneratorQueue.put(
('createChan', 4, 1, str_chan + ' ' + str(self.chanPassPhrase.text().toUtf8()),
self.chanPassPhrase.text().toUtf8(),
('createChan', 4, 1, str_chan + ' ' + ustr(self.chanPassPhrase.text()),
ustr(self.chanPassPhrase.text()).encode("utf-8", "replace"),
True))
else:
addressGeneratorQueue.put(
('joinChan', addBMIfNotPresent(self.chanAddress.text().toUtf8()),
str_chan + ' ' + str(self.chanPassPhrase.text().toUtf8()),
self.chanPassPhrase.text().toUtf8(),
('joinChan', addBMIfNotPresent(ustr(self.chanAddress.text())),
str_chan + ' ' + ustr(self.chanPassPhrase.text()),
ustr(self.chanPassPhrase.text()).encode("utf-8", "replace"),
True))
addressGeneratorReturnValue = apiAddressGeneratorReturnQueue.get(True)
if addressGeneratorReturnValue and addressGeneratorReturnValue[0] != 'chan name does not match address':
if len(addressGeneratorReturnValue) > 0 and addressGeneratorReturnValue[0] != 'chan name does not match address':
UISignalQueue.put(('updateStatusBar', _translate(
"newchandialog", "Successfully created / joined chan %1").arg(unicode(self.chanPassPhrase.text()))))
"newchandialog", "Successfully created / joined chan {0}").format(unic(ustr(self.chanPassPhrase.text())))))
self.parent.ui.tabWidget.setCurrentIndex(
self.parent.ui.tabWidget.indexOf(self.parent.ui.chans)
)
self.done(QtGui.QDialog.Accepted)
self.done(QtWidgets.QDialog.Accepted)
else:
UISignalQueue.put(('updateStatusBar', _translate("newchandialog", "Chan creation / joining failed")))
self.done(QtGui.QDialog.Rejected)
UISignalQueue.put((
'updateStatusBar',
_translate("newchandialog", "Chan creation / joining failed")
))
self.done(QtWidgets.QDialog.Rejected)
def reject(self):
"""Cancel joining the chan"""
self.timer.stop()
self.hide()
UISignalQueue.put(('updateStatusBar', _translate("newchandialog", "Chan creation / joining cancelled")))
self.done(QtGui.QDialog.Rejected)
UISignalQueue.put((
'updateStatusBar',
_translate("newchandialog", "Chan creation / joining cancelled")
))
self.done(QtWidgets.QDialog.Rejected)

View File

@ -1,20 +1,21 @@
from os import path
from PyQt4 import QtGui
from debug import logger
import widgets
from unqstr import ustr
import six
from bitmessageqt import widgets
from qtpy import QtWidgets
class RetranslateMixin(object):
def retranslateUi(self):
defaults = QtGui.QWidget()
defaults = QtWidgets.QWidget()
widgets.load(self.__class__.__name__.lower() + '.ui', defaults)
for attr, value in defaults.__dict__.iteritems():
for attr, value in six.iteritems(defaults.__dict__):
setTextMethod = getattr(value, "setText", None)
if callable(setTextMethod):
getattr(self, attr).setText(getattr(defaults, attr).text())
elif isinstance(value, QtGui.QTableWidget):
for i in range (value.columnCount()):
getattr(self, attr).setText(ustr(getattr(defaults, attr).text()))
elif isinstance(value, QtWidgets.QTableWidget):
for i in range(value.columnCount()):
getattr(self, attr).horizontalHeaderItem(i).setText(
getattr(defaults, attr).horizontalHeaderItem(i).text())
for i in range (value.rowCount()):
ustr(getattr(defaults, attr).horizontalHeaderItem(i).text()))
for i in range(value.rowCount()):
getattr(self, attr).verticalHeaderItem(i).setText(
getattr(defaults, attr).verticalHeaderItem(i).text())
ustr(getattr(defaults, attr).verticalHeaderItem(i).text()))

View File

@ -2,11 +2,11 @@
import inspect
import re
from HTMLParser import HTMLParser
from six.moves.html_parser import HTMLParser
from urllib import quote_plus
from urlparse import urlparse
from six.moves.urllib.parse import quote_plus, urlparse
from unqstr import ustr, unic
class SafeHTMLParser(HTMLParser):
"""HTML parser with sanitisation"""
@ -124,9 +124,9 @@ class SafeHTMLParser(HTMLParser):
def feed(self, data):
try:
data = unicode(data, 'utf-8')
except UnicodeDecodeError:
data = unicode(data, 'utf-8', errors='replace')
data = unic(ustr(data))
except TypeError:
pass
HTMLParser.feed(self, data)
tmp = SafeHTMLParser.replace_pre(data)
tmp = self.uriregex1.sub(r'<a href="\1">\1</a>', tmp)

View File

@ -1,13 +1,14 @@
"""
This module setting file is for settings
SettingsDialog class definition
"""
import ConfigParser
from six.moves import configparser
import os
import sys
import tempfile
from qtpy import QtCore, QtGui, QtWidgets
import six
from PyQt4 import QtCore, QtGui
from unqstr import ustr
import debug
import defaults
@ -16,7 +17,7 @@ import openclpow
import paths
import queues
import state
import widgets
from bitmessageqt import widgets
from bmconfigparser import config as config_obj
from helper_sql import sqlExecute, sqlStoredProcedure
from helper_startup import start_proxyconfig
@ -26,12 +27,18 @@ from network.asyncore_pollchoose import set_rates
from tr import _translate
try:
SafeConfigParser = configparser.SafeConfigParser
except AttributeError:
# alpine linux, python3.12
SafeConfigParser = configparser.ConfigParser
def getSOCKSProxyType(config):
"""Get user socksproxytype setting from *config*"""
try:
result = ConfigParser.SafeConfigParser.get(
result = SafeConfigParser.get(
config, 'bitmessagesettings', 'socksproxytype')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
except (configparser.NoSectionError, configparser.NoOptionError):
return None
else:
if result.lower() in ('', 'none', 'false'):
@ -39,14 +46,14 @@ def getSOCKSProxyType(config):
return result
class SettingsDialog(QtGui.QDialog):
class SettingsDialog(QtWidgets.QDialog):
"""The "Settings" dialog"""
# pylint: disable=too-many-instance-attributes
def __init__(self, parent=None, firstrun=False):
super(SettingsDialog, self).__init__(parent)
widgets.load('settings.ui', self)
self.app = QtGui.QApplication.instance()
self.app = QtWidgets.QApplication.instance()
self.parent = parent
self.firstrun = firstrun
self.config = config_obj
@ -83,14 +90,14 @@ class SettingsDialog(QtGui.QDialog):
self.tabWidgetSettings.setCurrentIndex(
self.tabWidgetSettings.indexOf(self.tabNetworkSettings)
)
QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
def adjust_from_config(self, config):
"""Adjust all widgets state according to config settings"""
# pylint: disable=too-many-branches,too-many-statements
current_style = self.app.get_windowstyle()
for i, sk in enumerate(QtGui.QStyleFactory.keys()):
for i, sk in enumerate(QtWidgets.QStyleFactory.keys()):
self.comboBoxStyle.addItem(sk)
if sk == current_style:
self.comboBoxStyle.setCurrentIndex(i)
@ -187,7 +194,7 @@ class SettingsDialog(QtGui.QDialog):
else:
if self.checkBoxOnionOnly.isChecked():
self.checkBoxOnionOnly.setText(
self.checkBoxOnionOnly.text() + ", " + _translate(
ustr(self.checkBoxOnionOnly.text()) + ", " + _translate(
"MainWindow", "may cause connection problems!"))
self.checkBoxOnionOnly.setStyleSheet(
"QCheckBox { color : red; }")
@ -324,10 +331,10 @@ class SettingsDialog(QtGui.QDialog):
_translate("MainWindow", "Testing..."))
nc = namecoin.namecoinConnection({
'type': self.getNamecoinType(),
'host': str(self.lineEditNamecoinHost.text().toUtf8()),
'port': str(self.lineEditNamecoinPort.text().toUtf8()),
'user': str(self.lineEditNamecoinUser.text().toUtf8()),
'password': str(self.lineEditNamecoinPassword.text().toUtf8())
'host': ustr(self.lineEditNamecoinHost.text()),
'port': ustr(self.lineEditNamecoinPort.text()),
'user': ustr(self.lineEditNamecoinUser.text()),
'password': ustr(self.lineEditNamecoinPassword.text())
})
status, text = nc.test()
self.labelNamecoinTestResult.setText(text)
@ -342,7 +349,7 @@ class SettingsDialog(QtGui.QDialog):
def choose_font(self):
"""Show the font selection dialog"""
font, valid = QtGui.QFontDialog.getFont()
font, valid = QtWidgets.QFontDialog.getFont()
if valid:
self.save_font_setting(font)
@ -372,7 +379,7 @@ class SettingsDialog(QtGui.QDialog):
self.config.set('bitmessagesettings', 'replybelow', str(
self.checkBoxReplyBelow.isChecked()))
window_style = str(self.comboBoxStyle.currentText())
window_style = ustr(self.comboBoxStyle.currentText())
if self.app.get_windowstyle() != window_style or self.config.safeGet(
'bitmessagesettings', 'font'
) != self.font_setting:
@ -386,8 +393,8 @@ class SettingsDialog(QtGui.QDialog):
" the window style or default font."), 1)
))
lang = str(self.languageComboBox.itemData(
self.languageComboBox.currentIndex()).toString())
lang = ustr(self.languageComboBox.itemData(
self.languageComboBox.currentIndex()))
self.config.set('bitmessagesettings', 'userlocale', lang)
self.parent.change_translation()
@ -469,7 +476,7 @@ class SettingsDialog(QtGui.QDialog):
self.config.set('bitmessagesettings', 'maxuploadrate', str(
int(float(self.lineEditMaxUploadRate.text()))))
except ValueError:
QtGui.QMessageBox.about(
QtWidgets.QMessageBox.about(
self, _translate("MainWindow", "Number needed"),
_translate(
"MainWindow",
@ -486,13 +493,13 @@ class SettingsDialog(QtGui.QDialog):
self.config.set(
'bitmessagesettings', 'namecoinrpctype', self.getNamecoinType())
self.config.set('bitmessagesettings', 'namecoinrpchost', str(
self.config.set('bitmessagesettings', 'namecoinrpchost', ustr(
self.lineEditNamecoinHost.text()))
self.config.set('bitmessagesettings', 'namecoinrpcport', str(
self.config.set('bitmessagesettings', 'namecoinrpcport', ustr(
self.lineEditNamecoinPort.text()))
self.config.set('bitmessagesettings', 'namecoinrpcuser', str(
self.config.set('bitmessagesettings', 'namecoinrpcuser', ustr(
self.lineEditNamecoinUser.text()))
self.config.set('bitmessagesettings', 'namecoinrpcpassword', str(
self.config.set('bitmessagesettings', 'namecoinrpcpassword', ustr(
self.lineEditNamecoinPassword.text()))
self.parent.resetNamecoinConnection()
@ -510,11 +517,11 @@ class SettingsDialog(QtGui.QDialog):
float(self.lineEditSmallMessageDifficulty.text())
* defaults.networkDefaultPayloadLengthExtraBytes)))
if self.comboBoxOpenCL.currentText().toUtf8() != self.config.safeGet(
'bitmessagesettings', 'opencl'):
if ustr(self.comboBoxOpenCL.currentText()) != ustr(self.config.safeGet(
'bitmessagesettings', 'opencl')):
self.config.set(
'bitmessagesettings', 'opencl',
str(self.comboBoxOpenCL.currentText()))
ustr(self.comboBoxOpenCL.currentText()))
queues.workerQueue.put(('resetPoW', ''))
acceptableDifficultyChanged = False
@ -593,7 +600,7 @@ class SettingsDialog(QtGui.QDialog):
if state.maximumLengthOfTimeToBotherResendingMessages < 432000:
# If the time period is less than 5 hours, we give
# zero values to all fields. No message will be sent again.
QtGui.QMessageBox.about(
QtWidgets.QMessageBox.about(
self,
_translate("MainWindow", "Will not resend ever"),
_translate(

View File

@ -1112,9 +1112,6 @@
<tabstop>checkBoxSocksListen</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="bitmessage_icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>

View File

@ -1,15 +1,16 @@
#!/usr/bin/python2.7
"""
src/settingsmixin.py
====================
"""
from PyQt4 import QtCore, QtGui
from unqstr import ustr
from qtpy import QtCore, QtWidgets
class SettingsMixin(object):
"""Mixin for adding geometry and state saving between restarts."""
"""Mixin for adding geometry and state saving between restarts"""
def warnIfNoObjectName(self):
"""
Handle objects which don't have a name. Currently it ignores them. Objects without a name can't have their
@ -40,8 +41,9 @@ class SettingsMixin(object):
self.warnIfNoObjectName()
settings = QtCore.QSettings()
try:
geom = settings.value("/".join([str(self.objectName()), "geometry"]))
target.restoreGeometry(geom.toByteArray() if hasattr(geom, 'toByteArray') else geom)
geom = settings.value(
"/".join([ustr(self.objectName()), "geometry"]))
target.restoreGeometry(geom)
except Exception:
pass
@ -50,14 +52,15 @@ class SettingsMixin(object):
self.warnIfNoObjectName()
settings = QtCore.QSettings()
try:
state = settings.value("/".join([str(self.objectName()), "state"]))
target.restoreState(state.toByteArray() if hasattr(state, 'toByteArray') else state)
state = settings.value("/".join([ustr(self.objectName()), "state"]))
target.restoreState(state)
except Exception:
pass
class SMainWindow(QtGui.QMainWindow, SettingsMixin):
"""Main window with Settings functionality."""
class SMainWindow(QtWidgets.QMainWindow, SettingsMixin):
"""Main window with Settings functionality"""
def loadSettings(self):
"""Load main window settings."""
self.readGeometry(self)
@ -69,9 +72,9 @@ class SMainWindow(QtGui.QMainWindow, SettingsMixin):
self.writeGeometry(self)
class STableWidget(QtGui.QTableWidget, SettingsMixin):
class STableWidget(QtWidgets.QTableWidget, SettingsMixin):
"""Table widget with Settings functionality"""
# pylint: disable=too-many-ancestors
def loadSettings(self):
"""Load table settings."""
self.readState(self.horizontalHeader())
@ -81,8 +84,9 @@ class STableWidget(QtGui.QTableWidget, SettingsMixin):
self.writeState(self.horizontalHeader())
class SSplitter(QtGui.QSplitter, SettingsMixin):
"""Splitter with Settings functionality."""
class SSplitter(QtWidgets.QSplitter, SettingsMixin):
"""Splitter with Settings functionality"""
def loadSettings(self):
"""Load splitter settings"""
self.readState(self)
@ -92,17 +96,17 @@ class SSplitter(QtGui.QSplitter, SettingsMixin):
self.writeState(self)
class STreeWidget(QtGui.QTreeWidget, SettingsMixin):
"""Tree widget with settings functionality."""
# pylint: disable=too-many-ancestors
class STreeWidget(QtWidgets.QTreeWidget, SettingsMixin):
"""Tree widget with settings functionality"""
def loadSettings(self):
"""Load tree settings."""
"""Load tree settings. Unimplemented."""
# recurse children
# self.readState(self)
pass
def saveSettings(self):
"""Save tree settings"""
"""Save tree settings. Unimplemented."""
# recurse children
# self.writeState(self)
pass

View File

@ -1,11 +1,11 @@
# pylint: disable=unused-argument
"""Status bar Module"""
"""BMStatusBar class definition"""
from time import time
from PyQt4 import QtGui
from qtpy import QtWidgets
class BMStatusBar(QtGui.QStatusBar):
class BMStatusBar(QtWidgets.QStatusBar):
"""Status bar with queue and priorities"""
duration = 10000
deleteAfter = 60
@ -16,21 +16,24 @@ class BMStatusBar(QtGui.QStatusBar):
self.timer = self.startTimer(BMStatusBar.duration)
self.iterator = 0
def timerEvent(self, event):
def timerEvent(self, event): # pylint: disable=unused-argument
"""an event handler which allows to queue and prioritise messages to
show in the status bar, for example if many messages come very quickly
after one another, it adds delays and so on"""
while len(self.important) > 0:
self.iterator += 1
try:
if time() > self.important[self.iterator][1] + BMStatusBar.deleteAfter:
if (
self.important[self.iterator][1]
+ BMStatusBar.deleteAfter < time()
):
del self.important[self.iterator]
self.iterator -= 1
continue
except IndexError:
self.iterator = -1
continue
super(BMStatusBar, self).showMessage(self.important[self.iterator][0], 0)
self.showMessage(self.important[self.iterator][0], 0)
break
def addImportant(self, message):

View File

@ -1,14 +1,15 @@
"""Composing support request message functions."""
# pylint: disable=no-member
import ctypes
import os
import ssl
import sys
import time
from PyQt4 import QtCore
from unqstr import ustr, unic
from dbcompat import dbstr
import account
from bitmessageqt import account
import defaults
import network.stats
import paths
@ -16,12 +17,12 @@ import proofofwork
import queues
import state
from bmconfigparser import config
from foldertree import AccountMixin
from .foldertree import AccountMixin
from helper_sql import sqlExecute, sqlQuery
from l10n import getTranslationLanguage
from openclpow import openclEnabled
from pyelliptic.openssl import OpenSSL
from settings import getSOCKSProxyType
from .settings import getSOCKSProxyType
from version import softwareVersion
from tr import _translate
@ -31,7 +32,7 @@ OLD_SUPPORT_ADDRESS = 'BM-2cTkCtMYkrSPwFTpgcBrMrf5d8oZwvMZWK'
SUPPORT_ADDRESS = 'BM-2cUdgkDDAahwPAU6oD2A7DnjqZz3hgY832'
SUPPORT_LABEL = _translate("Support", "PyBitmessage support")
SUPPORT_MY_LABEL = _translate("Support", "My new address")
SUPPORT_SUBJECT = 'Support request'
SUPPORT_SUBJECT = _translate("Support", "Support request")
SUPPORT_MESSAGE = _translate("Support", '''
You can use this message to send a report to one of the PyBitmessage core \
developers regarding PyBitmessage or the mailchuck.com email service. \
@ -55,6 +56,7 @@ Operating system: {}
Architecture: {}bit
Python Version: {}
OpenSSL Version: {}
Qt API: {}
Frozen: {}
Portable mode: {}
C PoW: {}
@ -67,28 +69,35 @@ Connected hosts: {}
def checkAddressBook(myapp):
sqlExecute('DELETE from addressbook WHERE address=?', OLD_SUPPORT_ADDRESS)
queryreturn = sqlQuery('SELECT * FROM addressbook WHERE address=?', SUPPORT_ADDRESS)
"""
Add "PyBitmessage support" address to address book, remove old one if found.
"""
sqlExecute('DELETE from addressbook WHERE address=?', dbstr(OLD_SUPPORT_ADDRESS))
queryreturn = sqlQuery(
'SELECT * FROM addressbook WHERE address=?', dbstr(SUPPORT_ADDRESS))
if queryreturn == []:
sqlExecute(
'INSERT INTO addressbook VALUES (?,?)',
SUPPORT_LABEL.toUtf8(), SUPPORT_ADDRESS)
dbstr(SUPPORT_LABEL), dbstr(SUPPORT_ADDRESS))
myapp.rerenderAddressBook()
def checkHasNormalAddress():
for address in config.addresses():
"""Returns first enabled normal address or False if not found."""
for address in config.addresses(True):
acct = account.accountClass(address)
if acct.type == AccountMixin.NORMAL and config.safeGetBoolean(address, 'enabled'):
if acct.type == AccountMixin.NORMAL and config.safeGetBoolean(
address, 'enabled'):
return address
return False
def createAddressIfNeeded(myapp):
"""Checks if user has any anabled normal address, creates new one if no."""
if not checkHasNormalAddress():
queues.addressGeneratorQueue.put((
'createRandomAddress', 4, 1,
str(SUPPORT_MY_LABEL.toUtf8()),
ustr(SUPPORT_MY_LABEL),
1, "", False,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes
@ -100,15 +109,20 @@ def createAddressIfNeeded(myapp):
def createSupportMessage(myapp):
"""
Prepare the support request message and switch to tab "Send"
"""
checkAddressBook(myapp)
address = createAddressIfNeeded(myapp)
if state.shutdown:
return
myapp.ui.lineEditSubject.setText(SUPPORT_SUBJECT)
addrIndex = myapp.ui.comboBoxSendFrom.findData(
address, QtCore.Qt.UserRole,
QtCore.Qt.MatchFixedString | QtCore.Qt.MatchCaseSensitive)
# addrIndex = myapp.ui.comboBoxSendFrom.findData(
# address, QtCore.Qt.UserRole,
# QtCore.Qt.MatchFixedString | QtCore.Qt.MatchCaseSensitive
# )
addrIndex = myapp.ui.comboBoxSendFrom.findData(address)
if addrIndex == -1: # something is very wrong
return
myapp.ui.comboBoxSendFrom.setCurrentIndex(addrIndex)
@ -119,15 +133,13 @@ def createSupportMessage(myapp):
if commit:
version += " GIT " + commit
os = sys.platform
if os == "win32":
windowsversion = sys.getwindowsversion()
os = "Windows " + str(windowsversion[0]) + "." + str(windowsversion[1])
if sys.platform.startswith("win"):
# pylint: disable=no-member
osname = "Windows %s.%s" % sys.getwindowsversion()[:2]
else:
try:
from os import uname
unixversion = uname()
os = unixversion[0] + " " + unixversion[2]
unixversion = os.uname()
osname = unixversion[0] + " " + unixversion[2]
except:
pass
architecture = "32" if ctypes.sizeof(ctypes.c_voidp) == 4 else "64"
@ -136,22 +148,26 @@ def createSupportMessage(myapp):
opensslversion = "%s (Python internal), %s (external for PyElliptic)" % (
ssl.OPENSSL_VERSION, OpenSSL._version)
qtapi = os.environ.get('QT_API', 'fallback')
frozen = "N/A"
if paths.frozen:
frozen = paths.frozen
portablemode = "True" if state.appdata == paths.lookupExeFolder() else "False"
portablemode = str(state.appdata == paths.lookupExeFolder())
cpow = "True" if proofofwork.bmpow else "False"
openclpow = str(
openclpow = ustr(
config.safeGet('bitmessagesettings', 'opencl')
) if openclEnabled() else "None"
locale = getTranslationLanguage()
socks = getSOCKSProxyType(config) or "N/A"
upnp = config.safeGet('bitmessagesettings', 'upnp', "N/A")
socks = getSOCKSProxyType(config) or 'N/A'
upnp = config.safeGet('bitmessagesettings', 'upnp', 'N/A')
connectedhosts = len(network.stats.connectedHostsList())
myapp.ui.textEditMessage.setText(unicode(SUPPORT_MESSAGE, 'utf-8').format(
version, os, architecture, pythonversion, opensslversion, frozen,
portablemode, cpow, openclpow, locale, socks, upnp, connectedhosts))
myapp.ui.textEditMessage.setText(unic(ustr(SUPPORT_MESSAGE).format(
version, osname, architecture, pythonversion, opensslversion, qtapi,
frozen, portablemode, cpow, openclpow, locale, socks, upnp,
connectedhosts
)))
# single msg tab
myapp.ui.tabWidgetSend.setCurrentIndex(

View File

@ -1,7 +1,7 @@
import helper_addressbook
from bitmessageqt.support import createAddressIfNeeded
from main import TestBase
from .main import TestBase
class TestAddressbook(TestBase):

View File

@ -3,7 +3,8 @@
import sys
import unittest
from PyQt4 import QtCore, QtGui
from qtpy import QtCore, QtWidgets
from six import string_types
from six.moves import queue
import bitmessageqt
@ -20,7 +21,7 @@ class TestBase(unittest.TestCase):
def setUp(self):
self.app = (
QtGui.QApplication.instance()
QtWidgets.QApplication.instance()
or bitmessageqt.BitmessageQtApplication(sys.argv))
self.window = self.app.activeWindow()
if not self.window:
@ -50,10 +51,7 @@ class TestMain(unittest.TestCase):
def test_translate(self):
"""Check the results of _translate() with various args"""
self.assertIsInstance(
_translate("MainWindow", "Test"),
QtCore.QString
)
self.assertIsInstance(_translate("MainWindow", "Test"), string_types)
class TestUISignaler(TestBase):

View File

@ -2,7 +2,7 @@
import threading
import time
from PyQt4 import QtCore, QtGui, QtTest
from qtpy import QtCore, QtWidgets, QtTest
from bmconfigparser import config
from bitmessageqt import settings
@ -48,8 +48,8 @@ class TestSettings(TestBase):
def call_font_dialog():
"""A function to get the open font dialog and accept it"""
font_dialog = QtGui.QApplication.activeModalWidget()
self.assertTrue(isinstance(font_dialog, QtGui.QFontDialog))
font_dialog = QtWidgets.QApplication.activeModalWidget()
self.assertTrue(isinstance(font_dialog, QtWidgets.QFontDialog))
selected_font = font_dialog.currentFont()
self.assertEqual(
config.safeGet('bitmessagesettings', 'font'), '{},{}'.format(
@ -64,7 +64,7 @@ class TestSettings(TestBase):
def click_font_button():
"""Use QtTest to click the button"""
QtTest.QTest.mouseClick(
self.dialog.buttonFont, QtCore.Qt.LeftButton)
self.dialog.buttonFont, QtCore.Qt.MouseButton.LeftButton)
style_count = style_control.count()
self.assertGreater(style_count, 1)

View File

@ -4,7 +4,9 @@ import sys
from shared import isAddressInMyAddressBook
from main import TestBase
from .main import TestBase
from unqstr import ustr
class TestSupport(TestBase):
@ -26,8 +28,8 @@ class TestSupport(TestBase):
self.assertEqual(
ui.tabWidget.currentIndex(), ui.tabWidget.indexOf(ui.send))
self.assertEqual(
ui.lineEditTo.text(), self.SUPPORT_ADDRESS)
ustr(ui.lineEditTo.text()), ustr(self.SUPPORT_ADDRESS))
self.assertEqual(
ui.lineEditSubject.text(), self.SUPPORT_SUBJECT)
ustr(ui.lineEditSubject.text()), ustr(self.SUPPORT_SUBJECT))
self.assertIn(
sys.version, ui.textEditMessage.toPlainText())

View File

@ -1,15 +1,35 @@
from PyQt4.QtCore import QThread, SIGNAL
import sys
from qtpy import QtCore
import queues
from network.node import Peer
class UISignaler(QThread):
class UISignaler(QtCore.QThread):
_instance = None
def __init__(self, parent=None):
QThread.__init__(self, parent)
writeNewAddressToTable = QtCore.Signal(str, str, str)
updateStatusBar = QtCore.Signal(object)
updateSentItemStatusByToAddress = QtCore.Signal(object, str)
updateSentItemStatusByAckdata = QtCore.Signal(object, str)
displayNewInboxMessage = QtCore.Signal(object, str, object, object, str)
displayNewSentMessage = QtCore.Signal(
object, str, str, object, object, str)
updateNetworkStatusTab = QtCore.Signal(bool, bool, Peer)
updateNumberOfMessagesProcessed = QtCore.Signal()
updateNumberOfPubkeysProcessed = QtCore.Signal()
updateNumberOfBroadcastsProcessed = QtCore.Signal()
setStatusIcon = QtCore.Signal(str)
changedInboxUnread = QtCore.Signal(str)
rerenderMessagelistFromLabels = QtCore.Signal()
rerenderMessagelistToLabels = QtCore.Signal()
rerenderAddressBook = QtCore.Signal()
rerenderSubscriptions = QtCore.Signal()
rerenderBlackWhiteList = QtCore.Signal()
removeInboxRowByMsgid = QtCore.Signal(str)
newVersionAvailable = QtCore.Signal(str)
displayAlert = QtCore.Signal(str, str, bool)
@classmethod
def get(cls):
@ -22,69 +42,59 @@ class UISignaler(QThread):
command, data = queues.UISignalQueue.get()
if command == 'writeNewAddressToTable':
label, address, streamNumber = data
self.emit(
SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
label,
address,
str(streamNumber))
self.writeNewAddressToTable.emit(
label, address, str(streamNumber))
elif command == 'updateStatusBar':
self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"), data)
self.updateStatusBar.emit(data)
elif command == 'updateSentItemStatusByToAddress':
toAddress, message = data
self.emit(SIGNAL(
"updateSentItemStatusByToAddress(PyQt_PyObject,PyQt_PyObject)"), toAddress, message)
self.updateSentItemStatusByToAddress.emit(toAddress, message)
elif command == 'updateSentItemStatusByAckdata':
ackData, message = data
self.emit(SIGNAL(
"updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"), ackData, message)
self.updateSentItemStatusByAckdata.emit(ackData, message)
elif command == 'displayNewInboxMessage':
inventoryHash, toAddress, fromAddress, subject, body = data
self.emit(SIGNAL(
"displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
inventoryHash, toAddress, fromAddress, subject, body)
self.displayNewInboxMessage.emit(
inventoryHash, toAddress, fromAddress,
subject, body)
elif command == 'displayNewSentMessage':
toAddress, fromLabel, fromAddress, subject, message, ackdata = data
self.emit(SIGNAL(
"displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
toAddress, fromLabel, fromAddress, subject, message, ackdata)
self.displayNewSentMessage.emit(
toAddress, fromLabel, fromAddress,
subject.decode('utf-8'), message, ackdata)
elif command == 'updateNetworkStatusTab':
outbound, add, destination = data
self.emit(
SIGNAL("updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
outbound,
add,
destination)
self.updateNetworkStatusTab.emit(outbound, add, destination)
elif command == 'updateNumberOfMessagesProcessed':
self.emit(SIGNAL("updateNumberOfMessagesProcessed()"))
self.updateNumberOfMessagesProcessed.emit()
elif command == 'updateNumberOfPubkeysProcessed':
self.emit(SIGNAL("updateNumberOfPubkeysProcessed()"))
self.updateNumberOfPubkeysProcessed.emit()
elif command == 'updateNumberOfBroadcastsProcessed':
self.emit(SIGNAL("updateNumberOfBroadcastsProcessed()"))
self.updateNumberOfBroadcastsProcessed.emit()
elif command == 'setStatusIcon':
self.emit(SIGNAL("setStatusIcon(PyQt_PyObject)"), data)
self.setStatusIcon.emit(data)
elif command == 'changedInboxUnread':
self.emit(SIGNAL("changedInboxUnread(PyQt_PyObject)"), data)
self.changedInboxUnread.emit(data)
elif command == 'rerenderMessagelistFromLabels':
self.emit(SIGNAL("rerenderMessagelistFromLabels()"))
self.rerenderMessagelistFromLabels.emit()
elif command == 'rerenderMessagelistToLabels':
self.emit(SIGNAL("rerenderMessagelistToLabels()"))
self.rerenderMessagelistToLabels.emit()
elif command == 'rerenderAddressBook':
self.emit(SIGNAL("rerenderAddressBook()"))
self.rerenderAddressBook.emit()
elif command == 'rerenderSubscriptions':
self.emit(SIGNAL("rerenderSubscriptions()"))
self.rerenderSubscriptions.emit()
elif command == 'rerenderBlackWhiteList':
self.emit(SIGNAL("rerenderBlackWhiteList()"))
self.rerenderBlackWhiteList.emit()
elif command == 'removeInboxRowByMsgid':
self.emit(SIGNAL("removeInboxRowByMsgid(PyQt_PyObject)"), data)
self.removeInboxRowByMsgid.emit(data)
elif command == 'newVersionAvailable':
self.emit(SIGNAL("newVersionAvailable(PyQt_PyObject)"), data)
self.newVersionAvailable.emit(data)
elif command == 'alert':
title, text, exitAfterUserClicksOk = data
self.emit(
SIGNAL("displayAlert(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"),
title,
text,
exitAfterUserClicksOk)
self.displayAlert.emit(title, text, exitAfterUserClicksOk)
else:
sys.stderr.write(
'Command sent to UISignaler not recognized: %s\n' % command)
'Command sent to UISignaler not recognized: %s\n'
% command
)

View File

@ -1,7 +1,7 @@
import hashlib
import os
from PyQt4 import QtGui
from qtpy import QtGui
import state
from addresses import addBMIfNotPresent
@ -30,16 +30,18 @@ def identiconize(address):
# 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 = config.get('bitmessagesettings', 'identiconsuffix')
data = addBMIfNotPresent(address) + config.get(
'bitmessagesettings', 'identiconsuffix')
data = data.encode("utf-8", "replace")
if identicon_lib[:len('qidenticon')] == 'qidenticon':
# 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
icon_hash = hashlib.md5(
addBMIfNotPresent(address) + identiconsuffix).hexdigest()
use_two_colors = identicon_lib[:len('qidenticon_two')] == 'qidenticon_two'
icon_hash = hashlib.md5(data).hexdigest()
use_two_colors = (
identicon_lib[:len('qidenticon_two')] == 'qidenticon_two')
opacity = int(
identicon_lib not in (
'qidenticon_x', 'qidenticon_two_x',
@ -63,8 +65,7 @@ def identiconize(address):
# https://github.com/azaghal/pydenticon
# note that it requires pillow (or PIL) to be installed:
# https://python-pillow.org/
idcon_render = Pydenticon(
addBMIfNotPresent(address) + identiconsuffix, size * 3)
idcon_render = Pydenticon(data, size * 3)
rendering = idcon_render._render()
data = rendering.convert("RGBA").tostring("raw", "RGBA")
qim = QtGui.QImage(data, size, size, QtGui.QImage.Format_ARGB32)
@ -81,7 +82,7 @@ def avatarize(address):
falls back to identiconize(address)
"""
idcon = QtGui.QIcon()
icon_hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest()
icon_hash = hashlib.md5(addBMIfNotPresent(address).encode("utf-8", "replace")).hexdigest()
if address == str_broadcast_subscribers:
# don't hash [Broadcast subscribers]
icon_hash = address
@ -105,11 +106,9 @@ def avatarize(address):
lower_default = state.appdata + 'avatars/' + 'default.' + ext.lower()
upper_default = state.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

View File

@ -1,13 +1,15 @@
from PyQt4 import uic
from qtpy import uic
import os.path
import paths
import sys
def resource_path(resFile):
baseDir = paths.codePath()
for subDir in ["ui", "bitmessageqt"]:
if os.path.isdir(os.path.join(baseDir, subDir)) and os.path.isfile(os.path.join(baseDir, subDir, resFile)):
return os.path.join(baseDir, subDir, resFile)
for subDir in ("ui", "bitmessageqt"):
path = os.path.join(baseDir, subDir, resFile)
if os.path.isfile(path):
return path
def load(resFile, widget):
uic.loadUi(resource_path(resFile), widget)

View File

@ -9,19 +9,24 @@ from datetime import datetime
from six import string_types
from six.moves import configparser
from unqstr import ustr
try:
import state
except ImportError:
from pybitmessage import state
SafeConfigParser = configparser.SafeConfigParser
try:
SafeConfigParser = configparser.SafeConfigParser
except AttributeError:
# alpine linux, python3.12
SafeConfigParser = configparser.ConfigParser
config_ready = Event()
class BMConfigParser(SafeConfigParser):
"""
Singleton class inherited from :class:`ConfigParser.SafeConfigParser`
Singleton class inherited from :class:`configparser.SafeConfigParser`
with additional methods specific to bitmessage config.
"""
# pylint: disable=too-many-ancestors
@ -114,7 +119,8 @@ class BMConfigParser(SafeConfigParser):
"""Return a list of local bitmessage addresses (from section labels)"""
sections = [x for x in self.sections() if x.startswith('BM-')]
if sort:
sections.sort(key=lambda item: self.get(item, 'label').lower())
sections.sort(key=lambda item: ustr(self.get(item, 'label')) \
.lower())
return sections
def save(self):

View File

@ -1,5 +1,5 @@
"""
A thread for creating addresses
addressGenerator thread class definition
"""
import time
@ -211,8 +211,8 @@ class addressGenerator(StoppableThread):
'updateStatusBar',
_translate(
"MainWindow",
"Generating %1 new addresses."
).arg(str(numberOfAddressesToMake))
"Generating {0} new addresses."
).format(str(numberOfAddressesToMake))
))
signingKeyNonce = 0
encryptionKeyNonce = 1
@ -302,9 +302,9 @@ class addressGenerator(StoppableThread):
'updateStatusBar',
_translate(
"MainWindow",
"%1 is already in 'Your Identities'."
"{0} is already in 'Your Identities'."
" Not adding it again."
).arg(address)
).format(address)
))
else:
self.logger.debug('label: %s', label)

View File

@ -12,6 +12,7 @@ import subprocess # nosec B404
import threading
import time
from binascii import hexlify
import sqlite3
import helper_bitcoin
import helper_inbox
@ -33,6 +34,7 @@ from helper_sql import (
from network import knownnodes, invQueue
from network.node import Peer
from tr import _translate
from dbcompat import dbstr
logger = logging.getLogger('default')
@ -121,7 +123,7 @@ class objectProcessor(threading.Thread):
objectType, data = queues.objectProcessorQueue.get()
sql.execute(
'INSERT INTO objectprocessorqueue VALUES (?,?)',
objectType, data)
objectType, sqlite3.Binary(data))
numberOfObjectsThatWereInTheObjectProcessorQueue += 1
logger.debug(
'Saved %s objects from the objectProcessorQueue to'
@ -140,19 +142,23 @@ class objectProcessor(threading.Thread):
# bypass nonce and time, retain object type/version/stream + body
readPosition = 16
if data[readPosition:] in state.ackdataForWhichImWatching:
data_bytes = bytes(data[readPosition:])
if data_bytes in state.ackdataForWhichImWatching:
logger.info('This object is an acknowledgement bound for me.')
del state.ackdataForWhichImWatching[data[readPosition:]]
sqlExecute(
del state.ackdataForWhichImWatching[data_bytes]
rowcount = sqlExecute(
"UPDATE sent SET status='ackreceived', lastactiontime=?"
" WHERE ackdata=?", int(time.time()), data[readPosition:])
" WHERE ackdata=?", int(time.time()), sqlite3.Binary(data_bytes))
if rowcount < 1:
rowcount = sqlExecute(
"UPDATE sent SET status='ackreceived', lastactiontime=?"
" WHERE ackdata=CAST(? AS TEXT)", int(time.time()), data_bytes)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
data[readPosition:],
_translate(
data_bytes, _translate(
"MainWindow",
"Acknowledgement of the message received %1"
).arg(l10n.formatTimestamp()))
"Acknowledgement of the message received {0}"
).format(l10n.formatTimestamp()))
))
else:
logger.debug('This object is not an acknowledgement bound for me.')
@ -215,9 +221,10 @@ class objectProcessor(threading.Thread):
logger.info(
'the hash requested in this getpubkey request is: %s',
hexlify(requestedHash))
requestedHash_bytes = bytes(requestedHash)
# if this address hash is one of mine
if requestedHash in shared.myAddressesByHash:
myAddress = shared.myAddressesByHash[requestedHash]
if requestedHash_bytes in shared.myAddressesByHash:
myAddress = shared.myAddressesByHash[requestedHash_bytes]
elif requestedAddressVersionNumber >= 4:
requestedTag = data[readPosition:readPosition + 32]
if len(requestedTag) != 32:
@ -227,8 +234,9 @@ class objectProcessor(threading.Thread):
logger.debug(
'the tag requested in this getpubkey request is: %s',
hexlify(requestedTag))
if requestedTag in shared.myAddressesByTag:
myAddress = shared.myAddressesByTag[requestedTag]
requestedTag_bytes = bytes(requestedTag)
if requestedTag_bytes in shared.myAddressesByTag:
myAddress = shared.myAddressesByTag[requestedTag_bytes]
if myAddress == '':
logger.info('This getpubkey request is not for any of my keys.')
@ -299,12 +307,12 @@ class objectProcessor(threading.Thread):
'(within processpubkey) payloadLength less than 146.'
' Sanity check failed.')
readPosition += 4
pubSigningKey = '\x04' + data[readPosition:readPosition + 64]
pubSigningKey = b'\x04' + data[readPosition:readPosition + 64]
# Is it possible for a public key to be invalid such that trying to
# encrypt or sign with it will cause an error? If it is, it would
# be easiest to test them here.
readPosition += 64
pubEncryptionKey = '\x04' + data[readPosition:readPosition + 64]
pubEncryptionKey = b'\x04' + data[readPosition:readPosition + 64]
if len(pubEncryptionKey) < 65:
return logger.debug(
'publicEncryptionKey length less than 64. Sanity check'
@ -327,19 +335,19 @@ class objectProcessor(threading.Thread):
queryreturn = sqlQuery(
"SELECT usedpersonally FROM pubkeys WHERE address=?"
" AND usedpersonally='yes'", address)
" AND usedpersonally='yes'", dbstr(address))
# if this pubkey is already in our database and if we have
# used it personally:
if queryreturn != []:
logger.info(
'We HAVE used this pubkey personally. Updating time.')
t = (address, addressVersion, dataToStore,
t = (dbstr(address), addressVersion, sqlite3.Binary(dataToStore),
int(time.time()), 'yes')
else:
logger.info(
'We have NOT used this pubkey personally. Inserting'
' in database.')
t = (address, addressVersion, dataToStore,
t = (dbstr(address), addressVersion, sqlite3.Binary(dataToStore),
int(time.time()), 'no')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
self.possibleNewPubkey(address)
@ -350,9 +358,9 @@ class objectProcessor(threading.Thread):
' Sanity check failed.')
return
readPosition += 4
pubSigningKey = '\x04' + data[readPosition:readPosition + 64]
pubSigningKey = b'\x04' + data[readPosition:readPosition + 64]
readPosition += 64
pubEncryptionKey = '\x04' + data[readPosition:readPosition + 64]
pubEncryptionKey = b'\x04' + data[readPosition:readPosition + 64]
readPosition += 64
specifiedNonceTrialsPerByteLength = decodeVarint(
data[readPosition:readPosition + 10])[1]
@ -389,20 +397,20 @@ class objectProcessor(threading.Thread):
address = encodeAddress(addressVersion, streamNumber, ripe)
queryreturn = sqlQuery(
"SELECT usedpersonally FROM pubkeys WHERE address=?"
" AND usedpersonally='yes'", address)
" AND usedpersonally='yes'", dbstr(address))
# if this pubkey is already in our database and if we have
# used it personally:
if queryreturn != []:
logger.info(
'We HAVE used this pubkey personally. Updating time.')
t = (address, addressVersion, dataToStore,
int(time.time()), 'yes')
t = (dbstr(address), addressVersion, sqlite3.Binary(dataToStore),
int(time.time()), dbstr('yes'))
else:
logger.info(
'We have NOT used this pubkey personally. Inserting'
' in database.')
t = (address, addressVersion, dataToStore,
int(time.time()), 'no')
t = (dbstr(address), addressVersion, sqlite3.Binary(dataToStore),
int(time.time()), dbstr('no'))
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
self.possibleNewPubkey(address)
@ -413,12 +421,13 @@ class objectProcessor(threading.Thread):
' Sanity check failed.')
tag = data[readPosition:readPosition + 32]
if tag not in state.neededPubkeys:
tag_bytes = bytes(tag)
if tag_bytes not in state.neededPubkeys:
return logger.info(
'We don\'t need this v4 pubkey. We didn\'t ask for it.')
# Let us try to decrypt the pubkey
toAddress = state.neededPubkeys[tag][0]
toAddress = state.neededPubkeys[tag_bytes][0]
if protocol.decryptAndCheckPubkeyPayload(data, toAddress) == \
'successful':
# At this point we know that we have been waiting on this
@ -483,7 +492,7 @@ class objectProcessor(threading.Thread):
# This is a message bound for me.
# Look up my address based on the RIPE hash.
toAddress = shared.myAddressesByHash[toRipe]
toAddress = shared.myAddressesByHash[bytes(toRipe)]
readPosition = 0
sendersAddressVersionNumber, sendersAddressVersionNumberLength = \
decodeVarint(decryptedData[readPosition:readPosition + 10])
@ -507,9 +516,9 @@ class objectProcessor(threading.Thread):
return
readPosition += sendersStreamNumberLength
readPosition += 4
pubSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64]
pubSigningKey = b'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
pubEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64]
pubEncryptionKey = b'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
if sendersAddressVersionNumber >= 3:
requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = \
@ -558,7 +567,7 @@ class objectProcessor(threading.Thread):
readPosition += signatureLengthLength
signature = decryptedData[
readPosition:readPosition + signatureLength]
signedData = data[8:20] + encodeVarint(1) + encodeVarint(
signedData = bytes(data[8:20]) + encodeVarint(1) + encodeVarint(
streamNumberAsClaimedByMsg
) + decryptedData[:positionOfBottomOfAckData]
@ -590,11 +599,11 @@ class objectProcessor(threading.Thread):
# person.
sqlExecute(
'''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
fromAddress,
dbstr(fromAddress),
sendersAddressVersionNumber,
decryptedData[:endOfThePublicKeyPosition],
sqlite3.Binary(decryptedData[:endOfThePublicKeyPosition]),
int(time.time()),
'yes')
dbstr('yes'))
# Check to see whether we happen to be awaiting this
# pubkey in order to send a message. If we are, it will do the POW
@ -631,7 +640,7 @@ class objectProcessor(threading.Thread):
'bitmessagesettings', 'blackwhitelist') == 'black':
queryreturn = sqlQuery(
"SELECT label FROM blacklist where address=? and enabled='1'",
fromAddress)
dbstr(fromAddress))
if queryreturn != []:
logger.info('Message ignored because address is in blacklist.')
@ -639,7 +648,7 @@ class objectProcessor(threading.Thread):
else: # We're using a whitelist
queryreturn = sqlQuery(
"SELECT label FROM whitelist where address=? and enabled='1'",
fromAddress)
dbstr(fromAddress))
if queryreturn == []:
logger.info(
'Message ignored because address not in whitelist.')
@ -808,13 +817,14 @@ class objectProcessor(threading.Thread):
elif broadcastVersion == 5:
embeddedTag = data[readPosition:readPosition + 32]
readPosition += 32
if embeddedTag not in shared.MyECSubscriptionCryptorObjects:
embeddedTag_bytes = bytes(embeddedTag)
if embeddedTag_bytes not in shared.MyECSubscriptionCryptorObjects:
logger.debug('We\'re not interested in this broadcast.')
return
# We are interested in this broadcast because of its tag.
# We're going to add some more data which is signed further down.
signedData = data[8:readPosition]
cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag]
signedData = bytes(data[8:readPosition])
cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag_bytes]
try:
decryptedData = cryptorObject.decrypt(data[readPosition:])
logger.debug('EC decryption successful')
@ -854,10 +864,10 @@ class objectProcessor(threading.Thread):
)
readPosition += sendersStreamLength
readPosition += 4
sendersPubSigningKey = '\x04' + \
sendersPubSigningKey = b'\x04' + \
decryptedData[readPosition:readPosition + 64]
readPosition += 64
sendersPubEncryptionKey = '\x04' + \
sendersPubEncryptionKey = b'\x04' + \
decryptedData[readPosition:readPosition + 64]
readPosition += 64
if sendersAddressVersion >= 3:
@ -927,11 +937,11 @@ class objectProcessor(threading.Thread):
# Let's store the public key in case we want to reply to this person.
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
fromAddress,
sendersAddressVersion,
decryptedData[:endOfPubkeyPosition],
dbstr(fromAddress),
dbstr(sendersAddressVersion),
sqlite3.Binary(decryptedData[:endOfPubkeyPosition]),
int(time.time()),
'yes')
dbstr('yes'))
# Check to see whether we happen to be awaiting this
# pubkey in order to send a message. If we are, it will do the POW
@ -997,8 +1007,9 @@ class objectProcessor(threading.Thread):
encodeVarint(addressVersion) + encodeVarint(streamNumber)
+ ripe
)[32:]
if tag in state.neededPubkeys:
del state.neededPubkeys[tag]
tag_bytes = bytes(tag)
if tag_bytes in state.neededPubkeys:
del state.neededPubkeys[tag_bytes]
self.sendMessages(address)
@staticmethod
@ -1012,7 +1023,7 @@ class objectProcessor(threading.Thread):
"UPDATE sent SET status='doingmsgpow', retrynumber=0"
" WHERE toaddress=?"
" AND (status='awaitingpubkey' OR status='doingpubkeypow')"
" AND folder='sent'", address)
" AND folder='sent'", dbstr(address))
queues.workerQueue.put(('sendmessage', ''))
@staticmethod
@ -1047,8 +1058,8 @@ class objectProcessor(threading.Thread):
if checksum != hashlib.sha512(payload).digest()[0:4]:
logger.info('ackdata checksum wrong. Not sending ackdata.')
return False
command = command.rstrip('\x00')
if command != 'object':
command = command.rstrip(b'\x00')
if command != b'object':
return False
return True

View File

@ -22,6 +22,7 @@ It resends messages when there has been no response:
import gc
import os
import time
import sqlite3
import queues
import state
@ -29,6 +30,7 @@ from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery
from network import connectionpool, knownnodes, StoppableThread
from tr import _translate
from dbcompat import dbstr
#: Equals 4 weeks. You could make this longer if you want
@ -99,6 +101,8 @@ class singleCleaner(StoppableThread):
tick - state.maximumLengthOfTimeToBotherResendingMessages
)
for toAddress, ackData, status in queryreturn:
toAddress = toAddress.decode("utf-8", "replace")
status = status.decode("utf-8", "replace")
if status == 'awaitingpubkey':
self.resendPubkeyRequest(toAddress)
elif status == 'msgsent':
@ -168,7 +172,7 @@ class singleCleaner(StoppableThread):
))
sqlExecute(
"UPDATE sent SET status = 'msgqueued'"
" WHERE toaddress = ? AND folder = 'sent'", address)
" WHERE toaddress = ? AND folder = 'sent'", dbstr(address))
queues.workerQueue.put(('sendmessage', ''))
def resendMsg(self, ackdata):
@ -177,9 +181,13 @@ class singleCleaner(StoppableThread):
'It has been a long time and we haven\'t heard an acknowledgement'
' to our msg. Sending again.'
)
rowcount = sqlExecute(
"UPDATE sent SET status = 'msgqueued'"
" WHERE ackdata = ? AND folder = 'sent'", sqlite3.Binary(ackdata))
if rowcount < 1:
sqlExecute(
"UPDATE sent SET status = 'msgqueued'"
" WHERE ackdata = ? AND folder = 'sent'", ackdata)
" WHERE ackdata = CAST(? AS TEXT) AND folder = 'sent'", ackdata)
queues.workerQueue.put(('sendmessage', ''))
queues.UISignalQueue.put((
'updateStatusBar',

View File

@ -11,6 +11,7 @@ import time
from binascii import hexlify, unhexlify
from struct import pack
from subprocess import call # nosec
import sqlite3
import defaults
import helper_inbox
@ -24,12 +25,15 @@ import protocol
import queues
import shared
import state
import tr
from addresses import decodeAddress, decodeVarint, encodeVarint
from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery
from network import knownnodes, StoppableThread, invQueue
from six.moves import configparser, queue
from six.moves.reprlib import repr
import six
from dbcompat import dbstr
from tr import _translate
def sizeof_fmt(num, suffix='h/s'):
@ -73,6 +77,7 @@ class singleWorker(StoppableThread):
'''SELECT DISTINCT toaddress FROM sent'''
''' WHERE (status='awaitingpubkey' AND folder='sent')''')
for toAddress, in queryreturn:
toAddress = toAddress.decode("utf-8", "replace")
toAddressVersionNumber, toStreamNumber, toRipe = \
decodeAddress(toAddress)[1:]
if toAddressVersionNumber <= 3:
@ -87,7 +92,7 @@ class singleWorker(StoppableThread):
tag = doubleHashOfAddressData[32:]
# We'll need this for when we receive a pubkey reply:
# it will be encrypted and we'll need to decrypt it.
state.neededPubkeys[tag] = (
state.neededPubkeys[bytes(tag)] = (
toAddress,
highlevelcrypto.makeCryptor(
hexlify(privEncryptionKey))
@ -99,17 +104,22 @@ class singleWorker(StoppableThread):
for row in queryreturn:
ackdata, = row
self.logger.info('Watching for ackdata %s', hexlify(ackdata))
state.ackdataForWhichImWatching[ackdata] = 0
state.ackdataForWhichImWatching[bytes(ackdata)] = 0
# Fix legacy (headerless) watched ackdata to include header
for oldack in state.ackdataForWhichImWatching:
if len(oldack) == 32:
# attach legacy header, always constant (msg/1/1)
newack = '\x00\x00\x00\x02\x01\x01' + oldack
state.ackdataForWhichImWatching[newack] = 0
sqlExecute(
newack = b'\x00\x00\x00\x02\x01\x01' + oldack
state.ackdataForWhichImWatching[bytes(newack)] = 0
rowcount = sqlExecute(
'''UPDATE sent SET ackdata=? WHERE ackdata=? AND folder = 'sent' ''',
newack, oldack
sqlite3.Binary(newack), sqlite3.Binary(oldack)
)
if rowcount < 1:
sqlExecute(
'''UPDATE sent SET ackdata=? WHERE ackdata=CAST(? AS TEXT) AND folder = 'sent' ''',
sqlite3.Binary(newack), oldack
)
del state.ackdataForWhichImWatching[oldack]
@ -217,9 +227,8 @@ class singleWorker(StoppableThread):
return privSigningKeyHex, privEncryptionKeyHex, \
pubSigningKey, pubEncryptionKey
def _doPOWDefaults(self, payload, TTL,
log_prefix='',
log_time=False):
def _doPOWDefaults(
self, payload, TTL, log_prefix='', log_time=False):
target = 2 ** 64 / (
defaults.networkDefaultProofOfWorkNonceTrialsPerByte * (
len(payload) + 8
@ -245,14 +254,16 @@ class singleWorker(StoppableThread):
'PoW took %.1f seconds, speed %s.',
delta, sizeof_fmt(nonce / delta)
)
except: # noqa:E722 # NameError
except NameError:
self.logger.warning("Proof of Work exception")
payload = pack('>Q', nonce) + payload
return payload
def doPOWForMyV2Pubkey(self, adressHash):
""" This function also broadcasts out the pubkey
message once it is done with the POW"""
"""
This function also broadcasts out the pubkey message once it is
done with the POW
"""
# Look up my stream number based on my address hash
myAddress = shared.myAddressesByHash[adressHash]
addressVersionNumber, streamNumber = decodeAddress(myAddress)[1:3]
@ -261,7 +272,7 @@ class singleWorker(StoppableThread):
TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', (embeddedTime))
payload += '\x00\x00\x00\x01' # object type: pubkey
payload += b'\x00\x00\x00\x01' # object type: pubkey
payload += encodeVarint(addressVersionNumber) # Address version number
payload += encodeVarint(streamNumber)
# bitfield of features supported by me (see the wiki).
@ -308,9 +319,10 @@ class singleWorker(StoppableThread):
def sendOutOrStoreMyV3Pubkey(self, adressHash):
"""
If this isn't a chan address, this function assembles the pubkey data, does the necessary POW and sends it out.
If it *is* a chan then it assembles the pubkey and stores is in the pubkey table so that we can send messages
to "ourselves".
If this isn't a chan address, this function assembles the pubkey
data, does the necessary POW and sends it out.
If it *is* a chan then it assembles the pubkey and stores it in
the pubkey table so that we can send messages to "ourselves".
"""
try:
myAddress = shared.myAddressesByHash[adressHash]
@ -338,7 +350,7 @@ class singleWorker(StoppableThread):
# expiresTime time.
payload = pack('>Q', (embeddedTime))
payload += '\x00\x00\x00\x01' # object type: pubkey
payload += b'\x00\x00\x00\x01' # object type: pubkey
payload += encodeVarint(addressVersionNumber) # Address version number
payload += encodeVarint(streamNumber)
# bitfield of features supported by me (see the wiki).
@ -396,9 +408,10 @@ class singleWorker(StoppableThread):
def sendOutOrStoreMyV4Pubkey(self, myAddress):
"""
It doesn't send directly anymore. It put is to a queue for another thread to send at an appropriate time,
whereas in the past it directly appended it to the outgoing buffer, I think. Same with all the other methods in
this class.
It doesn't send directly anymore. It put is to a queue for
another thread to send at an appropriate time, whereas in the
past it directly appended it to the outgoing buffer, I think.
Same with all the other methods in this class.
"""
if not config.has_section(myAddress):
# The address has been deleted.
@ -413,7 +426,7 @@ class singleWorker(StoppableThread):
TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', (embeddedTime))
payload += '\x00\x00\x00\x01' # object type: pubkey
payload += b'\x00\x00\x00\x01' # object type: pubkey
payload += encodeVarint(addressVersionNumber) # Address version number
payload += encodeVarint(streamNumber)
dataToEncrypt = protocol.getBitfield(myAddress)
@ -515,9 +528,15 @@ class singleWorker(StoppableThread):
payload, TTL, log_prefix='(For onionpeer object)')
inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
if six.PY2:
payload_buffer = buffer(payload)
tag_buffer = buffer(tag)
else: # assume six.PY3
payload_buffer = memoryview(payload)
tag_buffer = memoryview(tag)
state.Inventory[inventoryHash] = (
objectType, streamNumber, buffer(payload), # noqa: F821
embeddedTime, buffer(tag) # noqa: F821
objectType, streamNumber, payload_buffer, # noqa: F821
embeddedTime, tag_buffer # noqa: F821
)
self.logger.info(
'sending inv (within sendOnionPeerObj function) for object: %s',
@ -525,7 +544,10 @@ class singleWorker(StoppableThread):
invQueue.put((streamNumber, inventoryHash))
def sendBroadcast(self):
"""Send a broadcast-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
"""
Send a broadcast-type object (assemble the object, perform PoW
and put it to the inv announcement queue)
"""
# Reset just in case
sqlExecute(
'''UPDATE sent SET status='broadcastqueued' '''
@ -534,10 +556,13 @@ class singleWorker(StoppableThread):
queryreturn = sqlQuery(
'''SELECT fromaddress, subject, message, '''
''' ackdata, ttl, encodingtype FROM sent '''
''' WHERE status=? and folder='sent' ''', 'broadcastqueued')
''' WHERE status=? and folder='sent' ''', dbstr('broadcastqueued'))
for row in queryreturn:
fromaddress, subject, body, ackdata, TTL, encoding = row
fromaddress = fromaddress.decode("utf-8", "replace")
subject = subject.decode("utf-8", "replace")
body = body.decode("utf-8", "replace")
# status
_, addressVersionNumber, streamNumber, ripe = \
decodeAddress(fromaddress)
@ -556,8 +581,7 @@ class singleWorker(StoppableThread):
except ValueError:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Error! Could not find sender address"
" (your address) in the keys.dat file."))
@ -572,17 +596,24 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
_translate(
"MainWindow",
"Error, can't send."))
))
continue
if not sqlExecute(
rowcount = sqlExecute(
'''UPDATE sent SET status='doingbroadcastpow' '''
''' WHERE ackdata=? AND status='broadcastqueued' '''
''' AND folder='sent' ''',
ackdata):
sqlite3.Binary(ackdata))
if rowcount < 1:
rowcount = sqlExecute(
'''UPDATE sent SET status='doingbroadcastpow' '''
''' WHERE ackdata=CAST(? AS TEXT) AND status='broadcastqueued' '''
''' AND folder='sent' ''',
ackdata)
if rowcount < 1:
continue
# At this time these pubkeys are 65 bytes long
@ -599,7 +630,7 @@ class singleWorker(StoppableThread):
TTL = int(TTL + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', embeddedTime)
payload += '\x00\x00\x00\x03' # object type: broadcast
payload += b'\x00\x00\x00\x03' # object type: broadcast
if addressVersionNumber <= 3:
payload += encodeVarint(4) # broadcast version
@ -615,7 +646,7 @@ class singleWorker(StoppableThread):
tag = doubleHashOfAddressData[32:]
payload += tag
else:
tag = ''
tag = b''
dataToEncrypt = encodeVarint(addressVersionNumber)
dataToEncrypt += encodeVarint(streamNumber)
@ -661,8 +692,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Doing work necessary to send broadcast..."))
))
@ -694,23 +724,30 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Broadcast sent on %1"
).arg(l10n.formatTimestamp()))
ackdata, _translate(
"MainWindow", "Broadcast sent on {0}"
).format(l10n.formatTimestamp()))
))
# Update the status of the message in the 'sent' table to have
# a 'broadcastsent' status
sqlExecute(
rowcount = sqlExecute(
'''UPDATE sent SET msgid=?, status=?, lastactiontime=? '''
''' WHERE ackdata=? AND folder='sent' ''',
inventoryHash, 'broadcastsent', int(time.time()), ackdata
sqlite3.Binary(inventoryHash), dbstr('broadcastsent'), int(time.time()), sqlite3.Binary(ackdata)
)
if rowcount < 1:
sqlExecute(
'''UPDATE sent SET msgid=?, status=?, lastactiontime=? '''
''' WHERE ackdata=CAST(? AS TEXT) AND folder='sent' ''',
sqlite3.Binary(inventoryHash), 'broadcastsent', int(time.time()), ackdata
)
def sendMsg(self):
"""Send a message-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
"""
Send a message-type object (assemble the object, perform PoW
and put it to the inv announcement queue)
"""
# pylint: disable=too-many-nested-blocks
# Reset just in case
sqlExecute(
@ -726,6 +763,11 @@ class singleWorker(StoppableThread):
for row in queryreturn:
toaddress, fromaddress, subject, message, \
ackdata, status, TTL, retryNumber, encoding = row
toaddress = toaddress.decode("utf-8", "replace")
fromaddress = fromaddress.decode("utf-8", "replace")
subject = subject.decode("utf-8", "replace")
message = message.decode("utf-8", "replace")
status = status.decode("utf-8", "replace")
# toStatus
_, toAddressVersionNumber, toStreamNumber, toRipe = \
decodeAddress(toaddress)
@ -755,7 +797,7 @@ class singleWorker(StoppableThread):
if not sqlExecute(
'''UPDATE sent SET status='doingmsgpow' '''
''' WHERE toaddress=? AND status='msgqueued' AND folder='sent' ''',
toaddress
dbstr(toaddress)
):
continue
status = 'doingmsgpow'
@ -763,7 +805,7 @@ class singleWorker(StoppableThread):
# Let's see if we already have the pubkey in our pubkeys table
queryreturn = sqlQuery(
'''SELECT address FROM pubkeys WHERE address=?''',
toaddress
dbstr(toaddress)
)
# If we have the needed pubkey in the pubkey table already,
if queryreturn != []:
@ -771,7 +813,7 @@ class singleWorker(StoppableThread):
if not sqlExecute(
'''UPDATE sent SET status='doingmsgpow' '''
''' WHERE toaddress=? AND status='msgqueued' AND folder='sent' ''',
toaddress
dbstr(toaddress)
):
continue
status = 'doingmsgpow'
@ -783,31 +825,31 @@ class singleWorker(StoppableThread):
sqlExecute(
'''UPDATE pubkeys SET usedpersonally='yes' '''
''' WHERE address=?''',
toaddress
dbstr(toaddress)
)
# We don't have the needed pubkey in the pubkeys table already.
else:
if toAddressVersionNumber <= 3:
toTag = ''
toTag = b''
else:
toTag = highlevelcrypto.double_sha512(
encodeVarint(toAddressVersionNumber)
+ encodeVarint(toStreamNumber) + toRipe
)[32:]
toTag_bytes = bytes(toTag)
if toaddress in state.neededPubkeys or \
toTag in state.neededPubkeys:
toTag_bytes in state.neededPubkeys:
# We already sent a request for the pubkey
sqlExecute(
'''UPDATE sent SET status='awaitingpubkey', '''
''' sleeptill=? WHERE toaddress=? '''
''' AND status='msgqueued' ''',
int(time.time()) + 2.5 * 24 * 60 * 60,
toaddress
dbstr(toaddress)
)
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toaddress,
tr._translate(
toaddress, _translate(
"MainWindow",
"Encryption key was requested earlier."))
))
@ -836,7 +878,8 @@ class singleWorker(StoppableThread):
privEncryptionKey = doubleHashOfToAddressData[:32]
# The second half of the sha512 hash.
tag = doubleHashOfToAddressData[32:]
state.neededPubkeys[tag] = (
tag_bytes = bytes(tag)
state.neededPubkeys[tag_bytes] = (
toaddress,
highlevelcrypto.makeCryptor(
hexlify(privEncryptionKey))
@ -858,8 +901,8 @@ class singleWorker(StoppableThread):
''' status='awaitingpubkey' or '''
''' status='doingpubkeypow') AND '''
''' folder='sent' ''',
toaddress)
del state.neededPubkeys[tag]
dbstr(toaddress))
del state.neededPubkeys[tag_bytes]
break
# else:
# There was something wrong with this
@ -875,12 +918,11 @@ class singleWorker(StoppableThread):
'''UPDATE sent SET '''
''' status='doingpubkeypow' WHERE '''
''' toaddress=? AND status='msgqueued' AND folder='sent' ''',
toaddress
dbstr(toaddress)
)
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toaddress,
tr._translate(
toaddress, _translate(
"MainWindow",
"Sending a request for the"
" recipient\'s encryption key."))
@ -901,11 +943,10 @@ class singleWorker(StoppableThread):
# if we aren't sending this to ourselves or a chan
if not config.has_section(toaddress):
state.ackdataForWhichImWatching[ackdata] = 0
state.ackdataForWhichImWatching[bytes(ackdata)] = 0
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Looking up the receiver\'s public key"))
))
@ -920,7 +961,7 @@ class singleWorker(StoppableThread):
# is too hard then we'll abort.
queryreturn = sqlQuery(
'SELECT transmitdata FROM pubkeys WHERE address=?',
toaddress)
dbstr(toaddress))
for row in queryreturn: # pylint: disable=redefined-outer-name
pubkeyPayload, = row
@ -962,15 +1003,14 @@ class singleWorker(StoppableThread):
)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Problem: Destination is a mobile"
" device who requests that the"
" destination be included in the"
" message but this is disallowed in"
" your settings. %1"
).arg(l10n.formatTimestamp()))
" your settings. {0}"
).format(l10n.formatTimestamp()))
))
# if the human changes their setting and then
# sends another message or restarts their client,
@ -993,8 +1033,7 @@ class singleWorker(StoppableThread):
defaults.networkDefaultPayloadLengthExtraBytes
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Doing work necessary to send message.\n"
"There is no required difficulty for"
@ -1026,32 +1065,19 @@ class singleWorker(StoppableThread):
requiredAverageProofOfWorkNonceTrialsPerByte,
requiredPayloadLengthExtraBytes
)
queues.UISignalQueue.put(
(
'updateSentItemStatusByAckdata',
(
ackdata,
tr._translate(
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata, _translate(
"MainWindow",
"Doing work necessary to send message.\n"
"Receiver\'s required difficulty: %1"
" and %2"
).arg(
str(
"Receiver\'s required difficulty: {0} and {1}"
).format(
float(requiredAverageProofOfWorkNonceTrialsPerByte)
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte
)
).arg(
str(
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
float(requiredPayloadLengthExtraBytes)
/ defaults.networkDefaultPayloadLengthExtraBytes
)
)
)
)
)
))
))
if status != 'forcepow':
maxacceptablenoncetrialsperbyte = config.getint(
'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
@ -1065,24 +1091,30 @@ class singleWorker(StoppableThread):
if cond1 or cond2:
# The demanded difficulty is more than
# we are willing to do.
sqlExecute(
rowcount = sqlExecute(
'''UPDATE sent SET status='toodifficult' '''
''' WHERE ackdata=? AND folder='sent' ''',
sqlite3.Binary(ackdata))
if rowcount < 1:
sqlExecute(
'''UPDATE sent SET status='toodifficult' '''
''' WHERE ackdata=CAST(? AS TEXT) AND folder='sent' ''',
ackdata)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Problem: The work demanded by"
" the recipient (%1 and %2) is"
" more difficult than you are"
" willing to do. %3"
).arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte)
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
).arg(str(float(requiredPayloadLengthExtraBytes)
/ defaults.networkDefaultPayloadLengthExtraBytes)
).arg(l10n.formatTimestamp()))))
"Problem: The work demanded by the"
" recipient ({0} and {1}) is more"
" difficult than you are willing"
" to do. {2}"
).format(
float(requiredAverageProofOfWorkNonceTrialsPerByte)
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
float(requiredPayloadLengthExtraBytes)
/ defaults.networkDefaultPayloadLengthExtraBytes,
l10n.formatTimestamp()))
))
continue
else: # if we are sending a message to ourselves or a chan..
self.logger.info('Sending a message.')
@ -1096,15 +1128,14 @@ class singleWorker(StoppableThread):
except (configparser.NoSectionError, configparser.NoOptionError) as err:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"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()))
" message. {0}"
).format(l10n.formatTimestamp()))
))
self.logger.error(
'Error within sendMsg. Could not read the keys'
@ -1122,8 +1153,7 @@ class singleWorker(StoppableThread):
defaults.networkDefaultPayloadLengthExtraBytes
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Doing work necessary to send message."))
))
@ -1145,8 +1175,7 @@ class singleWorker(StoppableThread):
except ValueError:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Error! Could not find sender address"
" (your address) in the keys.dat file."))
@ -1161,7 +1190,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
_translate(
"MainWindow",
"Error, can't send."))
))
@ -1201,23 +1230,22 @@ class singleWorker(StoppableThread):
'Not bothering to include ackdata because we are'
' sending to ourselves or a chan.'
)
fullAckPayload = ''
fullAckPayload = b''
elif not protocol.checkBitfield(
behaviorBitfield, protocol.BITFIELD_DOESACK):
self.logger.info(
'Not bothering to include ackdata because'
' the receiver said that they won\'t relay it anyway.'
)
fullAckPayload = ''
fullAckPayload = b''
else:
# The fullAckPayload is a normal msg protocol message
# with the proof of work already completed that the
# receiver of this message can easily send out.
fullAckPayload = self.generateFullAckMessage(
ackdata, toStreamNumber, TTL)
fullAckPayload = self.generateFullAckMessage(ackdata, TTL)
payload += encodeVarint(len(fullAckPayload))
payload += fullAckPayload
dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \
dataToSign = pack('>Q', embeddedTime) + b'\x00\x00\x00\x02' + \
encodeVarint(1) + encodeVarint(toStreamNumber) + payload
signature = highlevelcrypto.sign(
dataToSign, privSigningKeyHex, self.digestAlg)
@ -1227,27 +1255,31 @@ class singleWorker(StoppableThread):
# We have assembled the data that will be encrypted.
try:
encrypted = highlevelcrypto.encrypt(
payload, "04" + hexlify(pubEncryptionKeyBase256)
payload, b"04" + hexlify(pubEncryptionKeyBase256)
)
except: # noqa:E722
self.logger.warning("highlevelcrypto.encrypt didn't work")
sqlExecute(
rowcount = sqlExecute(
'''UPDATE sent SET status='badkey' WHERE ackdata=? AND folder='sent' ''',
sqlite3.Binary(ackdata)
)
if rowcount < 1:
sqlExecute(
'''UPDATE sent SET status='badkey' WHERE ackdata=CAST(? AS TEXT) AND folder='sent' ''',
ackdata
)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Problem: The recipient\'s encryption key is"
" no good. Could not encrypt message. %1"
).arg(l10n.formatTimestamp()))
" no good. Could not encrypt message. {0}"
).format(l10n.formatTimestamp()))
))
continue
encryptedPayload = pack('>Q', embeddedTime)
encryptedPayload += '\x00\x00\x00\x02' # object type: msg
encryptedPayload += b'\x00\x00\x00\x02' # object type: msg
encryptedPayload += encodeVarint(1) # msg version
encryptedPayload += encodeVarint(toStreamNumber) + encrypted
target = 2 ** 64 / (
@ -1306,21 +1338,19 @@ class singleWorker(StoppableThread):
not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Message sent. Sent at %1"
).arg(l10n.formatTimestamp()))))
ackdata, _translate(
"MainWindow", "Message sent. Sent at {0}"
).format(l10n.formatTimestamp()))
))
else:
# not sending to a chan or one of my addresses
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Message sent. Waiting for acknowledgement."
" Sent on %1"
).arg(l10n.formatTimestamp()))
" Sent on {0}"
).format(l10n.formatTimestamp()))
))
self.logger.info(
'Broadcasting inv for my msg(within sendmsg function): %s',
@ -1337,10 +1367,17 @@ class singleWorker(StoppableThread):
newStatus = 'msgsent'
# wait 10% past expiration
sleepTill = int(time.time() + TTL * 1.1)
sqlExecute(
rowcount = sqlExecute(
'''UPDATE sent SET msgid=?, status=?, retrynumber=?, '''
''' sleeptill=?, lastactiontime=? WHERE ackdata=? AND folder='sent' ''',
inventoryHash, newStatus, retryNumber + 1,
sqlite3.Binary(inventoryHash), dbstr(newStatus), retryNumber + 1,
sleepTill, int(time.time()), sqlite3.Binary(ackdata)
)
if rowcount < 1:
sqlExecute(
'''UPDATE sent SET msgid=?, status=?, retrynumber=?, '''
''' sleeptill=?, lastactiontime=? WHERE ackdata=CAST(? AS TEXT) AND folder='sent' ''',
sqlite3.Binary(inventoryHash), newStatus, retryNumber + 1,
sleepTill, int(time.time()), ackdata
)
@ -1386,7 +1423,7 @@ class singleWorker(StoppableThread):
'''SELECT retrynumber FROM sent WHERE toaddress=? '''
''' AND (status='doingpubkeypow' OR status='awaitingpubkey') '''
''' AND folder='sent' LIMIT 1''',
toAddress
dbstr(toAddress)
)
if not queryReturn:
self.logger.critical(
@ -1412,10 +1449,11 @@ class singleWorker(StoppableThread):
privEncryptionKey = doubleHashOfAddressData[:32]
# Note that this is the second half of the sha512 hash.
tag = doubleHashOfAddressData[32:]
if tag not in state.neededPubkeys:
tag_bytes = bytes(tag)
if tag_bytes not in state.neededPubkeys:
# We'll need this for when we receive a pubkey reply:
# it will be encrypted and we'll need to decrypt it.
state.neededPubkeys[tag] = (
state.neededPubkeys[tag_bytes] = (
toAddress,
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
)
@ -1429,7 +1467,7 @@ class singleWorker(StoppableThread):
TTL = TTL + helper_random.randomrandrange(-300, 300)
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', embeddedTime)
payload += '\x00\x00\x00\x00' # object type: getpubkey
payload += b'\x00\x00\x00\x00' # object type: getpubkey
payload += encodeVarint(addressVersionNumber)
payload += encodeVarint(streamNumber)
if addressVersionNumber <= 3:
@ -1446,8 +1484,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put(('updateStatusBar', statusbar))
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toAddress,
tr._translate(
toAddress, _translate(
"MainWindow",
"Doing work necessary to request encryption key."))
))
@ -1468,33 +1505,34 @@ class singleWorker(StoppableThread):
''' status='awaitingpubkey', retrynumber=?, sleeptill=? '''
''' WHERE toaddress=? AND (status='doingpubkeypow' OR '''
''' status='awaitingpubkey') AND folder='sent' ''',
int(time.time()), retryNumber + 1, sleeptill, toAddress)
int(time.time()), retryNumber + 1, sleeptill, dbstr(toAddress))
queues.UISignalQueue.put((
'updateStatusBar',
tr._translate(
'updateStatusBar', _translate(
"MainWindow",
"Broadcasting the public key request. This program will"
" auto-retry if they are offline.")
))
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toAddress,
tr._translate(
toAddress, _translate(
"MainWindow",
"Sending public key request. Waiting for reply."
" Requested at %1"
).arg(l10n.formatTimestamp()))
" Requested at {0}"
).format(l10n.formatTimestamp()))
))
def generateFullAckMessage(self, ackdata, _, TTL):
"""
It might be perfectly fine to just use the same TTL for the ackdata that we use for the message. But I would
rather it be more difficult for attackers to associate ackData with the associated msg object. However, users
would want the TTL of the acknowledgement to be about the same as they set for the message itself. So let's set
the TTL of the acknowledgement to be in one of three 'buckets': 1 hour, 7 days, or 28 days, whichever is
relatively close to what the user specified.
"""
def generateFullAckMessage(self, ackdata, TTL):
"""Create ACK packet"""
# It might be perfectly fine to just use the same TTL for
# the ackdata that we use for the message. But I would rather
# it be more difficult for attackers to associate ackData with
# the associated msg object. However, users would want the TTL
# of the acknowledgement to be about the same as they set
# for the message itself. So let's set the TTL of the
# acknowledgement to be in one of three 'buckets': 1 hour, 7
# days, or 28 days, whichever is relatively close to what the
# user specified.
if TTL < 24 * 60 * 60: # 1 day
TTL = 24 * 60 * 60 # 1 day
elif TTL < 7 * 24 * 60 * 60: # 1 week
@ -1511,4 +1549,4 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For ack message)', log_time=True)
return protocol.CreatePacket('object', payload)
return protocol.CreatePacket(b'object', payload)

View File

@ -4,9 +4,9 @@ SMTP client thread for delivering emails
# pylint: disable=unused-variable
import smtplib
import urlparse
from six.moves.urllib import parse as urlparse
from email.header import Header
from email.mime.text import MIMEText
from six.moves import email_mime_text
import queues
import state
@ -55,7 +55,7 @@ class smtpDeliver(StoppableThread):
u = urlparse.urlparse(dest)
to = urlparse.parse_qs(u.query)['to']
client = smtplib.SMTP(u.hostname, u.port)
msg = MIMEText(body, 'plain', 'utf-8')
msg = email_mime_text(body, 'plain', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = fromAddress + '@' + SMTPDOMAIN
toLabel = map(

View File

@ -12,6 +12,7 @@ import threading
import time
from email.header import decode_header
from email.parser import Parser
import sqlite3
import queues
from addresses import decodeAddress
@ -20,6 +21,7 @@ from helper_ackPayload import genAckPayload
from helper_sql import sqlExecute
from network.threads import StoppableThread
from version import softwareVersion
from dbcompat import dbstr
SMTPDOMAIN = "bmaddr.lan"
LISTENPORT = 8425
@ -88,19 +90,19 @@ class smtpServerPyBitmessage(smtpd.SMTPServer):
ackdata = genAckPayload(streamNumber, stealthLevel)
sqlExecute(
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
'',
toAddress,
ripe,
fromAddress,
subject,
message,
ackdata,
sqlite3.Binary(b''),
dbstr(toAddress),
sqlite3.Binary(ripe),
dbstr(fromAddress),
dbstr(subject),
dbstr(message),
sqlite3.Binary(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',
dbstr('msgqueued'),
0, # retryNumber
'sent', # folder
dbstr('sent'), # folder
2, # encodingtype
# not necessary to have a TTL higher than 2 days
min(config.getint('bitmessagesettings', 'ttl'), 86400 * 2)

View File

@ -8,6 +8,7 @@ import sqlite3
import sys
import threading
import time
from six.moves.reprlib import repr
try:
import helper_sql
@ -38,7 +39,7 @@ class sqlThread(threading.Thread):
helper_sql.sql_available = True
config_ready.wait()
self.conn = sqlite3.connect(state.appdata + 'messages.dat')
self.conn.text_factory = str
self.conn.text_factory = bytes
self.cur = self.conn.cursor()
self.cur.execute('PRAGMA secure_delete = true')
@ -73,7 +74,7 @@ class sqlThread(threading.Thread):
'''INSERT INTO subscriptions VALUES'''
'''('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''')
self.cur.execute(
'''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''')
'''CREATE TABLE settings (key text, value blob, UNIQUE(key) ON CONFLICT REPLACE)''')
self.cur.execute('''INSERT INTO settings VALUES('version','11')''')
self.cur.execute('''INSERT INTO settings VALUES('lastvacuumtime',?)''', (
int(time.time()),))
@ -542,7 +543,7 @@ class sqlThread(threading.Thread):
shutil.move(
paths.lookupAppdataFolder() + 'messages.dat', paths.lookupExeFolder() + 'messages.dat')
self.conn = sqlite3.connect(paths.lookupExeFolder() + 'messages.dat')
self.conn.text_factory = str
self.conn.text_factory = bytes
self.cur = self.conn.cursor()
elif item == 'movemessagstoappdata':
logger.debug('the sqlThread is moving the messages.dat file to the Appdata folder.')
@ -568,7 +569,7 @@ class sqlThread(threading.Thread):
shutil.move(
paths.lookupExeFolder() + 'messages.dat', paths.lookupAppdataFolder() + 'messages.dat')
self.conn = sqlite3.connect(paths.lookupAppdataFolder() + 'messages.dat')
self.conn.text_factory = str
self.conn.text_factory = bytes
self.cur = self.conn.cursor()
elif item == 'deleteandvacuume':
self.cur.execute('''delete from inbox where folder='trash' ''')

22
src/dbcompat.py Normal file
View File

@ -0,0 +1,22 @@
import logging
import six
logger = logging.getLogger("default")
def dbstr(v):
if six.PY3:
if isinstance(v, str):
return v
elif isinstance(v, bytes):
return v.decode("utf-8", "replace")
logger.debug("unexpected type in dbstr(): {}".format(type(v)))
return v # hope this never happens..
else: # assume six.PY2
if isinstance(v, unicode):
return v.encode("utf-8", "replace")
elif isinstance(v, str):
return v
elif isinstance(v, bytes):
return str(v)
logger.debug("unexpected type in dbstr(): {}".format(type(v)))
return v # hope this never happens..

View File

@ -6,6 +6,7 @@ and suggest how it may be installed
import os
import re
import sys
import six
# Only really old versions of Python don't have sys.hexversion. We don't
# support them. The logging module was introduced in Python 2.3
@ -17,6 +18,7 @@ if not hasattr(sys, 'hexversion') or sys.hexversion < 0x20300F0:
)
import logging # noqa:E402
from distutils import version
import subprocess # nosec B404
from importlib import import_module
@ -53,23 +55,23 @@ PACKAGE_MANAGER = {
}
PACKAGES = {
"PyQt4": {
"OpenBSD": "py-qt4",
"FreeBSD": "py27-qt4",
"Debian": "python-qt4",
"Ubuntu": "python-qt4",
"Ubuntu 12": "python-qt4",
"Ubuntu 20": "",
"openSUSE": "python-qt",
"Fedora": "PyQt4",
"Guix": "python2-pyqt@4.11.4",
"Gentoo": "dev-python/PyQt4",
"qtpy": {
"OpenBSD": "py-qtpy",
"FreeBSD": "py27-QtPy",
"Debian": "python-qtpy",
"Ubuntu": "python-qtpy",
"Ubuntu 12": "python-qtpy",
"Ubuntu 20": "python-qtpy",
"openSUSE": "python-QtPy",
"Fedora": "python2-QtPy",
"Guix": "",
"Gentoo": "dev-python/QtPy",
"optional": True,
"description":
"You only need PyQt if you want to use the GUI."
"You only need qtpy if you want to use the GUI."
" When only running as a daemon, this can be skipped.\n"
"However, you would have to install it manually"
" because setuptools does not support PyQt."
"Also maybe you need to install PyQt5 or PyQt4"
" if your package manager not installs it as qtpy dependency"
},
"msgpack": {
"OpenBSD": "py-msgpack",
@ -156,19 +158,19 @@ detectOS.result = None
def detectOSRelease():
"""Detecting the release of OS"""
with open("/etc/os-release", 'r') as osRelease:
version = None
ver = None
for line in osRelease:
if line.startswith("NAME="):
detectOS.result = OS_RELEASE.get(
line.replace('"', '').split("=")[-1].strip().lower())
elif line.startswith("VERSION_ID="):
try:
version = float(line.split("=")[1].replace("\"", ""))
ver = float(line.split("=")[1].replace("\"", ""))
except ValueError:
pass
if detectOS.result == "Ubuntu" and version < 14:
if detectOS.result == "Ubuntu" and ver < 14:
detectOS.result = "Ubuntu 12"
elif detectOS.result == "Ubuntu" and version >= 20:
elif detectOS.result == "Ubuntu" and ver >= 20:
detectOS.result = "Ubuntu 20"
@ -191,7 +193,7 @@ def try_import(module, log_extra=False):
def check_ripemd160():
"""Check availability of the RIPEMD160 hash function"""
try:
from fallback import RIPEMD160Hash # pylint: disable=relative-import
from fallback import RIPEMD160Hash
except ImportError:
return False
return RIPEMD160Hash is not None
@ -380,33 +382,71 @@ def check_curses():
def check_pyqt():
"""Do pyqt dependency check.
Here we are checking for PyQt4 with its version, as for it require
PyQt 4.8 or later.
Here we are checking for qtpy with its version, as for it require
qtpy.
"""
# pylint: disable=no-member
try:
import qtpy
except ImportError:
logger.error(
'PyBitmessage requires qtpy, and PyQt5 or PyQt4, '
' PyQt 4.8 or later and Qt 4.7 or later.')
return False
sip_found = False
try:
import sip
sip.setapi("QString", 2)
sip.setapi("QVariant", 2)
sip_found = True
except ImportError:
pass
QtCore = try_import(
'PyQt4.QtCore', 'PyBitmessage requires PyQt 4.8 or later and Qt 4.7 or later.')
'qtpy.QtCore', 'PyBitmessage requires qtpy and Qt 4.7 or later.')
if not QtCore:
return False
try:
logger.info('PyQt Version: %s', QtCore.PYQT_VERSION_STR)
logger.info('Qt Version: %s', QtCore.QT_VERSION_STR)
except AttributeError:
logger.info('Can be PySide..')
try:
logger.info('Qt Version: %s', QtCore.__version__)
except AttributeError:
# Can be PySide..
pass
passed = True
if QtCore.PYQT_VERSION < 0x40800:
try:
if version.LooseVersion(QtCore.PYQT_VERSION_STR) < '4.8':
logger.error(
'This version of PyQt is too old. PyBitmessage requries'
' PyQt 4.8 or later.')
passed = False
if QtCore.QT_VERSION < 0x40700:
except AttributeError:
# Can be PySide..
pass
try:
if version.LooseVersion(QtCore.__version__) < '4.7':
logger.error(
'This version of Qt is too old. PyBitmessage requries'
' Qt 4.7 or later.')
passed = False
except AttributeError:
# Can be PySide..
pass
if passed and not sip_found:
logger.info("sip is not found although PyQt is found")
return False
return passed
def check_msgpack():
"""Do sgpack module check.
"""Do msgpack module check.
simply checking if msgpack package with all its dependency
is available or not as recommended for messages coding.
@ -435,14 +475,9 @@ def check_dependencies(verbose=False, optional=False):
logger.info('Python version: %s', sys.version)
if sys.hexversion < 0x20704F0:
logger.error(
'PyBitmessage requires Python 2.7.4 or greater'
' (but not Python 3+)')
'PyBitmessage requires Python 2.7.4 or greater.'
' Python 2.7.18 is recommended.')
has_all_dependencies = False
if sys.hexversion >= 0x3000000:
logger.error(
'PyBitmessage does not support Python 3+. Python 2.7.4'
' or greater is required. Python 2.7.18 is recommended.')
sys.exit()
# FIXME: This needs to be uncommented when more of the code is python3 compatible
# if sys.hexversion >= 0x3000000 and sys.hexversion < 0x3060000:

View File

@ -49,10 +49,12 @@ License: MIT
# pylint: disable=too-many-lines,too-many-branches,too-many-statements,global-statement,too-many-return-statements
# pylint: disable=unused-argument
import collections
import io
from collections import OrderedDict
from six.moves.collections_abc import Hashable
from six.moves import range as xrange
import struct
import sys
import six
__version__ = "2.4.1"
"Module version string"
@ -99,9 +101,9 @@ class Ext: # pylint: disable=old-style-class
if not isinstance(type, int) or not (type >= 0 and type <= 127):
raise TypeError("ext type out of range")
# Check data is type bytes
elif sys.version_info[0] == 3 and not isinstance(data, bytes):
elif six.PY3 and not isinstance(data, bytes):
raise TypeError("ext data is not type \'bytes\'")
elif sys.version_info[0] == 2 and not isinstance(data, str):
elif six.PY2 and not isinstance(data, str):
raise TypeError("ext data is not type \'str\'")
self.type = type
self.data = data
@ -125,7 +127,7 @@ class Ext: # pylint: disable=old-style-class
String representation of this Ext object.
"""
s = "Ext Object (Type: 0x%02x, Data: " % self.type
s += " ".join(["0x%02x" % ord(self.data[i:i + 1])
s += " ".join(["0x%02x" % six.byte2int(self.data[i:i + 1])
for i in xrange(min(len(self.data), 8))])
if len(self.data) > 8:
s += " ..."
@ -549,7 +551,7 @@ def _packb2(obj, **options):
'\x82\xa7compact\xc3\xa6schema\x00'
>>>
"""
fp = io.BytesIO()
fp = six.BytesIO()
_pack2(obj, fp, **options)
return fp.getvalue()
@ -582,7 +584,7 @@ def _packb3(obj, **options):
b'\x82\xa7compact\xc3\xa6schema\x00'
>>>
"""
fp = io.BytesIO()
fp = six.BytesIO()
_pack3(obj, fp, **options)
return fp.getvalue()
@ -599,7 +601,7 @@ def _read_except(fp, n):
def _unpack_integer(code, fp, options):
if (ord(code) & 0xe0) == 0xe0:
if (six.byte2int(code) & 0xe0) == 0xe0:
return struct.unpack("b", code)[0]
elif code == b'\xd0':
return struct.unpack("b", _read_except(fp, 1))[0]
@ -609,7 +611,7 @@ def _unpack_integer(code, fp, options):
return struct.unpack(">i", _read_except(fp, 4))[0]
elif code == b'\xd3':
return struct.unpack(">q", _read_except(fp, 8))[0]
elif (ord(code) & 0x80) == 0x00:
elif (six.byte2int(code) & 0x80) == 0x00:
return struct.unpack("B", code)[0]
elif code == b'\xcc':
return struct.unpack("B", _read_except(fp, 1))[0]
@ -619,21 +621,21 @@ def _unpack_integer(code, fp, options):
return struct.unpack(">I", _read_except(fp, 4))[0]
elif code == b'\xcf':
return struct.unpack(">Q", _read_except(fp, 8))[0]
raise Exception("logic error, not int: 0x%02x" % ord(code))
raise Exception("logic error, not int: 0x%02x" % six.byte2int(code))
def _unpack_reserved(code, fp, options):
if code == b'\xc1':
raise ReservedCodeException(
"encountered reserved code: 0x%02x" % ord(code))
"encountered reserved code: 0x%02x" % six.byte2int(code))
raise Exception(
"logic error, not reserved code: 0x%02x" % ord(code))
"logic error, not reserved code: 0x%02x" % six.byte2int(code))
def _unpack_nil(code, fp, options):
if code == b'\xc0':
return None
raise Exception("logic error, not nil: 0x%02x" % ord(code))
raise Exception("logic error, not nil: 0x%02x" % six.byte2int(code))
def _unpack_boolean(code, fp, options):
@ -641,7 +643,7 @@ def _unpack_boolean(code, fp, options):
return False
elif code == b'\xc3':
return True
raise Exception("logic error, not boolean: 0x%02x" % ord(code))
raise Exception("logic error, not boolean: 0x%02x" % six.byte2int(code))
def _unpack_float(code, fp, options):
@ -649,12 +651,12 @@ def _unpack_float(code, fp, options):
return struct.unpack(">f", _read_except(fp, 4))[0]
elif code == b'\xcb':
return struct.unpack(">d", _read_except(fp, 8))[0]
raise Exception("logic error, not float: 0x%02x" % ord(code))
raise Exception("logic error, not float: 0x%02x" % six.byte2int(code))
def _unpack_string(code, fp, options):
if (ord(code) & 0xe0) == 0xa0:
length = ord(code) & ~0xe0
if (six.byte2int(code) & 0xe0) == 0xa0:
length = six.byte2int(code) & ~0xe0
elif code == b'\xd9':
length = struct.unpack("B", _read_except(fp, 1))[0]
elif code == b'\xda':
@ -662,7 +664,7 @@ def _unpack_string(code, fp, options):
elif code == b'\xdb':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not string: 0x%02x" % ord(code))
raise Exception("logic error, not string: 0x%02x" % six.byte2int(code))
# Always return raw bytes in compatibility mode
global compatibility
@ -686,7 +688,7 @@ def _unpack_binary(code, fp, options):
elif code == b'\xc6':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not binary: 0x%02x" % ord(code))
raise Exception("logic error, not binary: 0x%02x" % six.byte2int(code))
return _read_except(fp, length)
@ -709,9 +711,9 @@ def _unpack_ext(code, fp, options):
elif code == b'\xc9':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not ext: 0x%02x" % ord(code))
raise Exception("logic error, not ext: 0x%02x" % six.byte2int(code))
ext = Ext(ord(_read_except(fp, 1)), _read_except(fp, length))
ext = Ext(six.byte2int(_read_except(fp, 1)), _read_except(fp, length))
# Unpack with ext handler, if we have one
ext_handlers = options.get("ext_handlers")
@ -722,14 +724,14 @@ def _unpack_ext(code, fp, options):
def _unpack_array(code, fp, options):
if (ord(code) & 0xf0) == 0x90:
length = (ord(code) & ~0xf0)
if (six.byte2int(code) & 0xf0) == 0x90:
length = (six.byte2int(code) & ~0xf0)
elif code == b'\xdc':
length = struct.unpack(">H", _read_except(fp, 2))[0]
elif code == b'\xdd':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not array: 0x%02x" % ord(code))
raise Exception("logic error, not array: 0x%02x" % six.byte2int(code))
return [_unpack(fp, options) for _ in xrange(length)]
@ -741,17 +743,17 @@ def _deep_list_to_tuple(obj):
def _unpack_map(code, fp, options):
if (ord(code) & 0xf0) == 0x80:
length = (ord(code) & ~0xf0)
if (six.byte2int(code) & 0xf0) == 0x80:
length = (six.byte2int(code) & ~0xf0)
elif code == b'\xde':
length = struct.unpack(">H", _read_except(fp, 2))[0]
elif code == b'\xdf':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not map: 0x%02x" % ord(code))
raise Exception("logic error, not map: 0x%02x" % six.byte2int(code))
d = {} if not options.get('use_ordered_dict') \
else collections.OrderedDict()
else OrderedDict()
for _ in xrange(length):
# Unpack key
k = _unpack(fp, options)
@ -759,7 +761,7 @@ def _unpack_map(code, fp, options):
if isinstance(k, list):
# Attempt to convert list into a hashable tuple
k = _deep_list_to_tuple(k)
elif not isinstance(k, collections.Hashable):
elif not isinstance(k, Hashable):
raise UnhashableKeyException(
"encountered unhashable key: %s, %s" % (str(k), str(type(k))))
elif k in d:
@ -911,7 +913,7 @@ def _unpackb2(s, **options):
"""
if not isinstance(s, (str, bytearray)):
raise TypeError("packed data must be type 'str' or 'bytearray'")
return _unpack(io.BytesIO(s), options)
return _unpack(six.BytesIO(s), options)
# For Python 3, expects a bytes object
@ -957,7 +959,7 @@ def _unpackb3(s, **options):
"""
if not isinstance(s, (bytes, bytearray)):
raise TypeError("packed data must be type 'bytes' or 'bytearray'")
return _unpack(io.BytesIO(s), options)
return _unpack(six.BytesIO(s), options)
#############################################################################
# Module Initialization
@ -990,7 +992,7 @@ def __init():
_float_precision = "single"
# Map packb and unpackb to the appropriate version
if sys.version_info[0] == 3:
if six.PY3:
pack = _pack3
packb = _packb3
dump = _pack3

View File

@ -4,11 +4,12 @@ Insert value into addressbook
from bmconfigparser import config
from helper_sql import sqlExecute
from dbcompat import dbstr
def insert(address, label):
"""perform insert into addressbook"""
if address not in config.addresses():
return sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', label, address) == 1
return sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', dbstr(label), dbstr(address)) == 1
return False

View File

@ -19,17 +19,17 @@ def calculateBitcoinAddressFromPubkey(pubkey):
sha = hashlib.new('sha256')
sha.update(pubkey)
ripe.update(sha.digest())
ripeWithProdnetPrefix = '\x00' + ripe.digest()
ripeWithProdnetPrefix = b'\x00' + ripe.digest()
checksum = hashlib.sha256(hashlib.sha256(
ripeWithProdnetPrefix).digest()).digest()[:4]
binaryBitcoinAddress = ripeWithProdnetPrefix + checksum
numberOfZeroBytesOnBinaryBitcoinAddress = 0
while binaryBitcoinAddress[0] == '\x00':
while binaryBitcoinAddress[0] == b'\x00':
numberOfZeroBytesOnBinaryBitcoinAddress += 1
binaryBitcoinAddress = binaryBitcoinAddress[1:]
base58encoded = arithmetic.changebase(binaryBitcoinAddress, 256, 58)
return "1" * numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded
return b"1" * numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded
def calculateTestnetAddressFromPubkey(pubkey):
@ -43,14 +43,14 @@ def calculateTestnetAddressFromPubkey(pubkey):
sha = hashlib.new('sha256')
sha.update(pubkey)
ripe.update(sha.digest())
ripeWithProdnetPrefix = '\x6F' + ripe.digest()
ripeWithProdnetPrefix = b'\x6F' + ripe.digest()
checksum = hashlib.sha256(hashlib.sha256(
ripeWithProdnetPrefix).digest()).digest()[:4]
binaryBitcoinAddress = ripeWithProdnetPrefix + checksum
numberOfZeroBytesOnBinaryBitcoinAddress = 0
while binaryBitcoinAddress[0] == '\x00':
while binaryBitcoinAddress[0] == b'\x00':
numberOfZeroBytesOnBinaryBitcoinAddress += 1
binaryBitcoinAddress = binaryBitcoinAddress[1:]
base58encoded = arithmetic.changebase(binaryBitcoinAddress, 256, 58)
return "1" * numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded
return b"1" * numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded

View File

@ -1,12 +1,16 @@
"""Helper Inbox performs inbox messages related operations"""
import sqlite3
import queues
from helper_sql import sqlExecute, sqlQuery
from dbcompat import dbstr
def insert(t):
"""Perform an insert into the "inbox" table"""
sqlExecute('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?,?)''', *t)
u = [sqlite3.Binary(t[0]), dbstr(t[1]), dbstr(t[2]), dbstr(t[3]), dbstr(t[4]), dbstr(t[5]), dbstr(t[6]), t[7], t[8], sqlite3.Binary(t[9])]
sqlExecute('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?,?)''', *u)
# shouldn't emit changedInboxUnread and displayNewInboxMessage
# at the same time
# queues.UISignalQueue.put(('changedInboxUnread', None))
@ -14,22 +18,31 @@ def insert(t):
def trash(msgid):
"""Mark a message in the `inbox` as `trash`"""
sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', msgid)
rowcount = sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', sqlite3.Binary(msgid))
if rowcount < 1:
sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=CAST(? AS TEXT)''', msgid)
queues.UISignalQueue.put(('removeInboxRowByMsgid', msgid))
def delete(ack_data):
"""Permanent delete message from trash"""
sqlExecute("DELETE FROM inbox WHERE msgid = ?", ack_data)
rowcount = sqlExecute("DELETE FROM inbox WHERE msgid = ?", sqlite3.Binary(ack_data))
if rowcount < 1:
sqlExecute("DELETE FROM inbox WHERE msgid = CAST(? AS TEXT)", ack_data)
def undeleteMessage(msgid):
"""Undelte the message"""
sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=?''', msgid)
rowcount = sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=?''', sqlite3.Binary(msgid))
if rowcount < 1:
sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=CAST(? AS TEXT)''', msgid)
def isMessageAlreadyInInbox(sigHash):
"""Check for previous instances of this message"""
queryReturn = sqlQuery(
'''SELECT COUNT(*) FROM inbox WHERE sighash=?''', sigHash)
'''SELECT COUNT(*) FROM inbox WHERE sighash=?''', sqlite3.Binary(sigHash))
if len(queryReturn) < 1:
queryReturn = sqlQuery(
'''SELECT COUNT(*) FROM inbox WHERE sighash=CAST(? AS TEXT)''', sigHash)
return queryReturn[0][0] != 0

View File

@ -2,7 +2,6 @@
Message encoding end decoding functions
"""
import string
import zlib
import messagetypes
@ -71,7 +70,8 @@ class MsgEncode(object):
def encodeSimple(self, message):
"""Handle simple encoding"""
self.data = 'Subject:%(subject)s\nBody:%(body)s' % message
data = 'Subject:%(subject)s\nBody:%(body)s' % message
self.data = data.encode("utf-8", "replace")
self.length = len(self.data)
def encodeTrivial(self, message):
@ -99,14 +99,14 @@ class MsgDecode(object):
def decodeExtended(self, data):
"""Handle extended encoding"""
dc = zlib.decompressobj()
tmp = ""
tmp = b""
while len(tmp) <= config.safeGetInt("zlib", "maxsize"):
try:
got = dc.decompress(
data, config.safeGetInt("zlib", "maxsize")
+ 1 - len(tmp))
# EOF
if got == "":
if got == b"":
break
tmp += got
data = dc.unconsumed_tail
@ -142,7 +142,7 @@ class MsgDecode(object):
def decodeSimple(self, data):
"""Handle simple encoding"""
bodyPositionIndex = string.find(data, '\nBody:')
bodyPositionIndex = data.find(b'\nBody:')
if bodyPositionIndex > 1:
subject = data[8:bodyPositionIndex]
# Only save and show the first 500 characters of the subject.
@ -150,10 +150,11 @@ class MsgDecode(object):
subject = subject[:500]
body = data[bodyPositionIndex + 6:]
else:
subject = ''
subject = b''
body = data
# Throw away any extra lines (headers) after the subject.
if subject:
subject = subject.splitlines()[0]
self.subject = subject
self.body = body
# Field types should be the same for all message types
self.subject = subject.decode("utf-8", "replace")
self.body = body.decode("utf-8", "replace")

View File

@ -5,6 +5,7 @@ Used by :mod:`.bitmessageqt`.
from helper_sql import sqlQuery
from tr import _translate
from dbcompat import dbstr
def search_sql(
@ -52,23 +53,23 @@ def search_sql(
if account is not None:
if xAddress == 'both':
sqlStatementParts.append('(fromaddress = ? OR toaddress = ?)')
sqlArguments.append(account)
sqlArguments.append(account)
sqlArguments.append(dbstr(account))
sqlArguments.append(dbstr(account))
else:
sqlStatementParts.append(xAddress + ' = ? ')
sqlArguments.append(account)
sqlArguments.append(dbstr(account))
if folder is not None:
if folder == 'new':
folder = 'inbox'
unreadOnly = True
sqlStatementParts.append('folder = ? ')
sqlArguments.append(folder)
sqlArguments.append(dbstr(folder))
else:
sqlStatementParts.append('folder != ?')
sqlArguments.append('trash')
sqlArguments.append(dbstr('trash'))
if what:
sqlStatementParts.append('%s LIKE ?' % (where))
sqlArguments.append(what)
sqlArguments.append(dbstr(what))
if unreadOnly:
sqlStatementParts.append('read = 0')
if sqlStatementParts:

View File

@ -4,10 +4,12 @@ Insert values into sent table
import time
import uuid
import sqlite3
from addresses import decodeAddress
from bmconfigparser import config
from helper_ackPayload import genAckPayload
from helper_sql import sqlExecute, sqlQuery
from dbcompat import dbstr
# pylint: disable=too-many-arguments
@ -38,8 +40,8 @@ def insert(msgid=None, toAddress='[Broadcast subscribers]', fromAddress=None, su
ttl = ttl if ttl else config.getint('bitmessagesettings', 'ttl')
t = (msgid, toAddress, ripe, fromAddress, subject, message, ackdata,
sentTime, lastActionTime, sleeptill, status, retryNumber, folder,
t = (sqlite3.Binary(msgid), dbstr(toAddress), sqlite3.Binary(ripe), dbstr(fromAddress), dbstr(subject), dbstr(message), sqlite3.Binary(ackdata),
sentTime, lastActionTime, sleeptill, dbstr(status), retryNumber, dbstr(folder),
encoding, ttl)
sqlExecute('''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', *t)
@ -50,13 +52,19 @@ def insert(msgid=None, toAddress='[Broadcast subscribers]', fromAddress=None, su
def delete(ack_data):
"""Perform Delete query"""
sqlExecute("DELETE FROM sent WHERE ackdata = ?", ack_data)
rowcount = sqlExecute("DELETE FROM sent WHERE ackdata = ?", sqlite3.Binary(ack_data))
if rowcount < 1:
sqlExecute("DELETE FROM sent WHERE ackdata = CAST(? AS TEXT)", ack_data)
def retrieve_message_details(ack_data):
"""Retrieving Message details"""
data = sqlQuery(
"select toaddress, fromaddress, subject, message, received from inbox where msgid = ?", ack_data
"select toaddress, fromaddress, subject, message, received from inbox where msgid = ?", sqlite3.Binary(ack_data)
)
if len(data) < 1:
data = sqlQuery(
"select toaddress, fromaddress, subject, message, received from inbox where msgid = CAST(? AS TEXT)", ack_data
)
return data
@ -64,6 +72,10 @@ def retrieve_message_details(ack_data):
def trash(ackdata):
"""Mark a message in the `sent` as `trash`"""
rowcount = sqlExecute(
'''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdata
'''UPDATE sent SET folder='trash' WHERE ackdata=?''', sqlite3.Binary(ackdata)
)
if rowcount < 1:
rowcount = sqlExecute(
'''UPDATE sent SET folder='trash' WHERE ackdata=CAST(? AS TEXT)''', ackdata
)
return rowcount

View File

@ -61,7 +61,7 @@ def sqlQuery(sql_statement, *args):
return queryreturn
def sqlExecuteChunked(sql_statement, idCount, *args):
def sqlExecuteChunked(sql_statement, as_text, idCount, *args):
"""Execute chunked SQL statement to avoid argument limit"""
# SQLITE_MAX_VARIABLE_NUMBER,
# unfortunately getting/setting isn't exposed to python
@ -80,6 +80,15 @@ def sqlExecuteChunked(sql_statement, idCount, *args):
chunk_slice = args[
i:i + sqlExecuteChunked.chunkSize - (len(args) - idCount)
]
if as_text:
q = ""
n = len(chunk_slice)
for i in range(n):
q += "CAST(? AS TEXT)"
if i != n - 1:
q += ","
sqlSubmitQueue.put(sql_statement.format(q))
else:
sqlSubmitQueue.put(
sql_statement.format(','.join('?' * len(chunk_slice)))
)

View File

@ -7,6 +7,8 @@ High level cryptographic functions based on `.pyelliptic` OpenSSL bindings.
`More discussion. <https://github.com/yann2192/pyelliptic/issues/32>`_
"""
from unqstr import unic
import hashlib
import os
from binascii import hexlify
@ -102,7 +104,7 @@ def random_keys():
def deterministic_keys(passphrase, nonce):
"""Generate keys from *passphrase* and *nonce* (encoded as varint)"""
priv = hashlib.sha512(passphrase + nonce).digest()[:32]
priv = hashlib.sha512(unic(passphrase).encode("utf-8", "replace") + nonce).digest()[:32]
pub = pointMult(priv)
return priv, pub

View File

@ -28,8 +28,6 @@ class Inventory:
# cheap inheritance copied from asyncore
def __getattr__(self, attr):
if attr == "__contains__":
self.numberOfInventoryLookupsPerformed += 1
try:
realRet = getattr(self._realInventory, attr)
except AttributeError:
@ -40,6 +38,10 @@ class Inventory:
else:
return realRet
def __contains__(self, key):
self.numberOfInventoryLookupsPerformed += 1
return key in self._realInventory
# hint for pylint: this is dictionary like object
def __getitem__(self, key):
return self._realInventory[key]

View File

@ -3,8 +3,8 @@
import logging
import os
import re
import sys
import time
import six
from six.moves import range
@ -61,7 +61,7 @@ if not re.search(r'\d', time.strftime(time_format)):
# It seems some systems lie about the encoding they use
# so we perform comprehensive decoding tests
elif sys.version_info[0] == 2:
elif six.PY2:
try:
# Check day names
for i in range(7):
@ -118,7 +118,7 @@ def formatTimestamp(timestamp=None):
except ValueError:
timestring = time.strftime(time_format)
if sys.version_info[0] == 2:
if six.PY2:
return timestring.decode(encoding)
return timestring

View File

@ -1,10 +1,10 @@
"""
Namecoin queries
"""
# pylint: disable=too-many-branches,protected-access
# pylint: disable=too-many-branches
import base64
import httplib
from six.moves import http_client as httplib
import json
import os
import socket
@ -14,14 +14,14 @@ import defaults
from addresses import decodeAddress
from bmconfigparser import config
from debug import logger
from tr import _translate # translate
from tr import _translate
configSection = "bitmessagesettings"
class RPCError(Exception):
"""Error thrown when the RPC call returns an error."""
error = None
def __init__(self, data):
@ -29,7 +29,7 @@ class RPCError(Exception):
self.error = data
def __str__(self):
return "{0}: {1}".format(type(self).__name__, self.error)
return '{0}: {1}'.format(type(self).__name__, self.error)
class namecoinConnection(object):
@ -69,7 +69,7 @@ class namecoinConnection(object):
self.user = options["user"]
self.password = options["password"]
assert self.nmctype == "namecoind" or self.nmctype == "nmcontrol"
assert self.nmctype in ("namecoind", "nmcontrol")
if self.nmctype == "namecoind":
self.con = httplib.HTTPConnection(self.host, self.port, timeout=3)
@ -96,8 +96,8 @@ class namecoinConnection(object):
res = res["reply"]
if not res:
return (_translate(
"MainWindow", "The name %1 was not found."
).arg(identity.decode("utf-8", "ignore")), None)
"MainWindow", "The name {0} was not found."
).format(identity.decode("utf-8", "ignore")), None)
else:
assert False
except RPCError as exc:
@ -107,12 +107,12 @@ class namecoinConnection(object):
else:
errmsg = exc.error
return (_translate(
"MainWindow", "The namecoin query failed (%1)"
).arg(errmsg.decode("utf-8", "ignore")), None)
"MainWindow", "The namecoin query failed ({0})"
).format(errmsg.decode("utf-8", "ignore")), None)
except AssertionError:
return (_translate(
"MainWindow", "Unknown namecoin interface type: %1"
).arg(self.nmctype.decode("utf-8", "ignore")), None)
"MainWindow", "Unknown namecoin interface type: {0}"
).format(self.nmctype.decode("utf-8", "ignore")), None)
except Exception:
logger.exception("Namecoin query exception")
return (_translate(
@ -135,8 +135,8 @@ class namecoinConnection(object):
) if valid else (
_translate(
"MainWindow",
"The name %1 has no associated Bitmessage address."
).arg(identity.decode("utf-8", "ignore")), None)
"The name {0} has no associated Bitmessage address."
).format(identity.decode("utf-8", "ignore")), None)
def test(self):
"""
@ -160,11 +160,10 @@ class namecoinConnection(object):
versStr = "0.%d.%d" % (v1, v2)
else:
versStr = "0.%d.%d.%d" % (v1, v2, v3)
message = (
"success",
_translate(
return (
'success', _translate(
"MainWindow",
"Success! Namecoind version %1 running.").arg(
"Success! Namecoind version {0} running.").format(
versStr.decode("utf-8", "ignore")))
elif self.nmctype == "nmcontrol":
@ -172,32 +171,24 @@ class namecoinConnection(object):
prefix = "Plugin data running"
if ("reply" in res) and res["reply"][:len(prefix)] == prefix:
return (
"success",
_translate(
'success', _translate(
"MainWindow",
"Success! NMControll is up and running."
)
"Success! NMControll is up and running.")
)
logger.error("Unexpected nmcontrol reply: %s", res)
message = (
"failed",
_translate(
"MainWindow",
"Couldn\'t understand NMControl."
)
return (
'failed', _translate(
"MainWindow", "Couldn\'t understand NMControl.")
)
else:
sys.exit("Unsupported Namecoin type")
return message
except Exception:
logger.info("Namecoin connection test failure")
return (
"failed",
_translate(
'failed', _translate(
"MainWindow", "The connection to namecoin failed.")
)
@ -245,26 +236,24 @@ class namecoinConnection(object):
"Authorization", "Basic %s" % base64.b64encode(authstr))
self.con.endheaders()
self.con.send(data)
except: # noqa:E722
logger.info("HTTP connection error")
return None
try:
resp = self.con.getresponse()
result = resp.read()
if resp.status != 200:
raise Exception(
"Namecoin returned status"
" %i: %s" % (resp.status, resp.reason))
"Namecoin returned status %i: %s" %
(resp.status, resp.reason))
except: # noqa:E722
logger.info("HTTP receive error")
return None
except: # noqa:E722
logger.info("HTTP connection error")
return result
def queryServer(self, data):
"""Helper routine sending data to the RPC "
"server and returning the result."""
"""
Helper routine sending data to the RPC server and returning the result.
"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@ -296,23 +285,24 @@ def lookupNamecoinFolder():
"""
app = "namecoin"
from os import path, environ
if sys.platform == "darwin":
if "HOME" in environ:
dataFolder = path.join(os.environ["HOME"],
"Library/Application Support/", app) + "/"
else:
try:
dataFolder = os.path.join(
os.getenv("HOME"), "Library/Application Support/", app)
except TypeError: # getenv is None
sys.exit(
"Could not find home folder, please report this message"
" and your OS X version to the BitMessage Github."
) # TODO: remove exits from utility modules
dataFolder = (
os.path.join(os.getenv("APPDATA"), app)
if sys.platform.startswith('win') else
os.path.join(os.getenv("HOME"), ".%s" % app)
)
elif "win32" in sys.platform or "win64" in sys.platform:
dataFolder = path.join(environ["APPDATA"], app) + "\\"
else:
dataFolder = path.join(environ["HOME"], ".%s" % app) + "/"
return dataFolder
return dataFolder + os.path.sep
def ensureNamecoinOptions():
@ -357,8 +347,8 @@ def ensureNamecoinOptions():
nmc.close()
except IOError:
logger.warning(
"%s unreadable or missing, Namecoin support deactivated",
nmcConfig)
"%s unreadable or missing, Namecoin support deactivated", nmcConfig
)
except Exception:
logger.warning("Error processing namecoin.conf", exc_info=True)

View File

@ -20,7 +20,7 @@ __all__ = ["StoppableThread"]
def start(config, state):
"""Start network threads"""
from .announcethread import AnnounceThread
import connectionpool # pylint: disable=relative-import
from network import connectionpool
from .addrthread import AddrThread
from .downloadthread import DownloadThread
from .invthread import InvThread

View File

@ -5,11 +5,11 @@ import random
from six.moves import queue
# magic imports!
import connectionpool
from network import connectionpool
from protocol import assembleAddrMessage
from network import addrQueue # FIXME: init with queue
from threads import StoppableThread
from .threads import StoppableThread
class AddrThread(StoppableThread):

View File

@ -7,7 +7,7 @@ import time
import network.asyncore_pollchoose as asyncore
import state
from threads import BusyError, nonBlocking
from .threads import BusyError, nonBlocking
class ProcessingError(Exception):

View File

@ -4,12 +4,12 @@ Announce myself (node address)
import time
# magic imports!
import connectionpool
from network import connectionpool
from bmconfigparser import config
from protocol import assembleAddrMessage
from node import Peer
from threads import StoppableThread
from .node import Peer
from .threads import StoppableThread
class AnnounceThread(StoppableThread):

View File

@ -19,6 +19,7 @@ from errno import (
ENOTCONN, ENOTSOCK, EPIPE, ESHUTDOWN, ETIMEDOUT, EWOULDBLOCK, errorcode
)
from threading import current_thread
from six.moves.reprlib import repr
try:
@ -723,21 +724,6 @@ class dispatcher(object):
if why.args[0] not in (ENOTCONN, EBADF):
raise
# cheap inheritance, used to pass all other attribute
# references to the underlying socket object.
def __getattr__(self, attr):
try:
retattr = getattr(self.socket, attr)
except AttributeError:
raise AttributeError(
"%s instance has no attribute '%s'"
% (self.__class__.__name__, attr))
else:
msg = "%(me)s.%(attr)s is deprecated; use %(me)s.socket.%(attr)s"\
" instead" % {'me': self.__class__.__name__, 'attr': attr}
warnings.warn(msg, DeprecationWarning, stacklevel=2)
return retattr
# log and log_info may be overridden to provide more sophisticated
# logging and warning methods. In general, log is for 'hit' logging
# and 'log_info' is for informational, warning and error logging.

View File

@ -6,7 +6,7 @@ import time
import protocol
import state
import connectionpool
import network.connectionpool # use long name to address recursive import
from network import dandelion_ins
from highlevelcrypto import calculateInventoryHash
@ -100,7 +100,7 @@ class BMObject(object): # pylint: disable=too-many-instance-attributes
logger.warning(
'The object has invalid stream: %s', self.streamNumber)
raise BMObjectInvalidError()
if self.streamNumber not in connectionpool.pool.streams:
if self.streamNumber not in network.connectionpool.pool.streams:
logger.debug(
'The streamNumber %i isn\'t one we are interested in.',
self.streamNumber)

View File

@ -9,13 +9,14 @@ import re
import socket
import struct
import time
import six
# magic imports!
import addresses
import knownnodes
from network import knownnodes
import protocol
import state
import connectionpool
import network.connectionpool # use long name to address recursive import
from bmconfigparser import config
from queues import objectProcessorQueue
from randomtrackingdict import RandomTrackingDict
@ -26,14 +27,27 @@ from network.bmobject import (
BMObjectUnwantedStreamError
)
from network.proxy import ProxyError
from network import dandelion_ins, invQueue, portCheckerQueue
from node import Node, Peer
from objectracker import ObjectTracker, missingObjects
from .node import Node, Peer
from .objectracker import ObjectTracker, missingObjects
logger = logging.getLogger('default')
def _hoststr(v):
if six.PY3:
return v
else: # assume six.PY2
return str(v)
def _restr(v):
if six.PY3:
return v.decode("utf-8", "replace")
else: # assume six.PY2
return v
class BMProtoError(ProxyError):
"""A Bitmessage Protocol Base Error"""
errorCodes = ("Protocol error")
@ -82,7 +96,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
"""Process incoming header"""
self.magic, self.command, self.payloadLength, self.checksum = \
protocol.Header.unpack(self.read_buf[:protocol.Header.size])
self.command = self.command.rstrip('\x00')
self.command = self.command.rstrip(b'\x00')
if self.magic != protocol.magic:
# skip 1 byte in order to sync
self.set_state("bm_header", length=1)
@ -107,7 +121,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.invalid = True
retval = True
if not self.fullyEstablished and self.command not in (
"error", "version", "verack"):
b"error", b"version", b"verack"):
logger.error(
'Received command %s before connection was fully'
' established, ignoring', self.command)
@ -115,7 +129,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if not self.invalid:
try:
retval = getattr(
self, "bm_command_" + str(self.command).lower())()
self, "bm_command_" + self.command.decode("utf-8", "replace").lower())()
except AttributeError:
# unimplemented command
logger.debug('unimplemented command %s', self.command)
@ -168,17 +182,17 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
"""Decode node details from the payload"""
# protocol.checkIPAddress()
services, host, port = self.decode_payload_content("Q16sH")
if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43':
if host[0:12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
host = socket.inet_ntop(socket.AF_INET, _hoststr(host[12:16]))
elif host[0:6] == b'\xfd\x87\xd8\x7e\xeb\x43':
# Onion, based on BMD/bitcoind
host = base64.b32encode(host[6:]).lower() + ".onion"
host = base64.b32encode(host[6:]).lower() + b".onion"
else:
host = socket.inet_ntop(socket.AF_INET6, str(host))
if host == "":
host = socket.inet_ntop(socket.AF_INET6, _hoststr(host))
if host == b"":
# This can happen on Windows systems which are not 64-bit
# compatible so let us drop the IPv6 address.
host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
host = socket.inet_ntop(socket.AF_INET, _hoststr(host[12:16]))
return Node(services, host, port)
@ -334,7 +348,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if now < self.skipUntil:
return True
for i in items:
self.pendingUpload[str(i)] = now
self.pendingUpload[i] = now
return True
def _command_inv(self, extend_dandelion_stem=False):
@ -353,7 +367,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if extend_dandelion_stem and not dandelion_ins.enabled:
return True
for i in map(str, items):
for i in items:
if i in state.Inventory and not dandelion_ins.hasHash(i):
continue
if extend_dandelion_stem and not dandelion_ins.hasHash(i):
@ -409,13 +423,17 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
try:
self.object.checkObjectByType()
if six.PY2:
data_buffer = buffer(self.object.data)
else: # assume six.PY3
data_buffer = memoryview(self.object.data)
objectProcessorQueue.put((
self.object.objectType, buffer(self.object.data))) # noqa: F821
self.object.objectType, data_buffer)) # noqa: F821
except BMObjectInvalidError:
BMProto.stopDownloadingObject(self.object.inventoryHash, True)
else:
try:
del missingObjects[self.object.inventoryHash]
del missingObjects[bytes(self.object.inventoryHash)]
except KeyError:
pass
@ -424,10 +442,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
dandelion_ins.removeHash(
self.object.inventoryHash, "cycle detection")
if six.PY2:
object_buffer = buffer(self.payload[objectOffset:])
tag_buffer = buffer(self.object.tag)
else: # assume six.PY3
object_buffer = memoryview(self.payload[objectOffset:])
tag_buffer = memoryview(self.object.tag)
state.Inventory[self.object.inventoryHash] = (
self.object.objectType, self.object.streamNumber,
buffer(self.payload[objectOffset:]), self.object.expiresTime, # noqa: F821
buffer(self.object.tag) # noqa: F821
object_buffer, self.object.expiresTime, # noqa: F821
tag_buffer # noqa: F821
)
self.handleReceivedObject(
self.object.streamNumber, self.object.inventoryHash)
@ -443,11 +467,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
"""Incoming addresses, process them"""
# not using services
for seenTime, stream, _, ip, port in self._decode_addr():
ip = str(ip)
if (
stream not in connectionpool.pool.streams
stream not in network.connectionpool.pool.streams
# FIXME: should check against complete list
or ip.startswith('bootstrap')
or ip.decode("utf-8", "replace").startswith('bootstrap')
):
continue
decodedIP = protocol.checkIPAddress(ip)
@ -477,7 +500,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
def bm_command_ping(self):
"""Incoming ping, respond to it."""
self.append_write_buf(protocol.CreatePacket('pong'))
self.append_write_buf(protocol.CreatePacket(b'pong'))
return True
@staticmethod
@ -526,21 +549,21 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
logger.debug(
'remote node incoming address: %s:%i',
self.destination.host, self.peerNode.port)
logger.debug('user agent: %s', self.userAgent)
logger.debug('user agent: %s', self.userAgent.decode("utf-8", "replace"))
logger.debug('streams: [%s]', ','.join(map(str, self.streams)))
if not self.peerValidityChecks():
# ABORT afterwards
return True
self.append_write_buf(protocol.CreatePacket('verack'))
self.append_write_buf(protocol.CreatePacket(b'verack'))
self.verackSent = True
ua_valid = re.match(
r'^/[a-zA-Z]+:[0-9]+\.?[\w\s\(\)\./:;-]*/$', self.userAgent)
r'^/[a-zA-Z]+:[0-9]+\.?[\w\s\(\)\./:;-]*/$', _restr(self.userAgent))
if not ua_valid:
self.userAgent = '/INVALID:0/'
self.userAgent = b'/INVALID:0/'
if not self.isOutbound:
self.append_write_buf(protocol.assembleVersionMessage(
self.destination.host, self.destination.port,
connectionpool.pool.streams, dandelion_ins.enabled, True,
network.connectionpool.pool.streams, dandelion_ins.enabled, True,
nodeid=self.nodeid))
logger.debug(
'%(host)s:%(port)i sending version',
@ -596,7 +619,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
'Closed connection to %s because there is no overlapping'
' interest in streams.', self.destination)
return False
if connectionpool.pool.inboundConnections.get(
if network.connectionpool.pool.inboundConnections.get(
self.destination):
try:
if not protocol.checkSocksIP(self.destination.host):
@ -614,8 +637,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# or server full report the same error to counter deanonymisation
if (
Peer(self.destination.host, self.peerNode.port)
in connectionpool.pool.inboundConnections
or len(connectionpool.pool)
in network.connectionpool.pool.inboundConnections
or len(network.connectionpool.pool)
> config.safeGetInt(
'bitmessagesettings', 'maxtotalconnections')
+ config.safeGetInt(
@ -627,7 +650,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
'Closed connection to %s due to server full'
' or duplicate inbound/outbound.', self.destination)
return False
if connectionpool.pool.isAlreadyConnected(self.nonce):
if network.connectionpool.pool.isAlreadyConnected(self.nonce):
self.append_write_buf(protocol.assembleErrorMessage(
errorText="I'm connected to myself. Closing connection.",
fatal=2))
@ -641,7 +664,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
@staticmethod
def stopDownloadingObject(hashId, forwardAnyway=False):
"""Stop downloading object *hashId*"""
for connection in connectionpool.pool.connections():
for connection in network.connectionpool.pool.connections():
try:
del connection.objectsNewToMe[hashId]
except KeyError:
@ -653,7 +676,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
except KeyError:
pass
try:
del missingObjects[hashId]
del missingObjects[bytes(hashId)]
except KeyError:
pass

View File

@ -7,7 +7,7 @@ import random
from six.moves import queue
import knownnodes
from network import knownnodes
import protocol
import state
@ -17,10 +17,16 @@ from network import portCheckerQueue
logger = logging.getLogger('default')
def _ends_with(s, tail):
try:
return s.endswith(tail)
except:
return s.decode("utf-8", "replace").endswith(tail)
def getDiscoveredPeer():
"""Get a peer from the local peer discovery list"""
try:
peer = random.choice(state.discoveredPeers.keys()) # nosec B311
peer = random.choice(list(state.discoveredPeers.keys())) # nosec B311
except (IndexError, KeyError):
raise ValueError
try:
@ -48,7 +54,7 @@ def chooseConnection(stream):
return getDiscoveredPeer()
for _ in range(50):
peer = random.choice( # nosec B311
knownnodes.knownNodes[stream].keys())
list(knownnodes.knownNodes[stream].keys()))
try:
peer_info = knownnodes.knownNodes[stream][peer]
if peer_info.get('self'):
@ -60,10 +66,10 @@ def chooseConnection(stream):
if haveOnion:
# do not connect to raw IP addresses
# --keep all traffic within Tor overlay
if onionOnly and not peer.host.endswith('.onion'):
if onionOnly and not _ends_with(peer.host, '.onion'):
continue
# onion addresses have a higher priority when SOCKS
if peer.host.endswith('.onion') and rating > 0:
if _ends_with(peer.host, '.onion') and rating > 0:
rating = 1
# TODO: need better check
elif not peer.host.startswith('bootstrap'):

View File

@ -9,22 +9,28 @@ import sys
import time
import random
import asyncore_pollchoose as asyncore
import knownnodes
from network import asyncore_pollchoose as asyncore
from network import knownnodes
import protocol
import state
from bmconfigparser import config
from connectionchooser import chooseConnection
from node import Peer
from proxy import Proxy
from tcp import (
from .connectionchooser import chooseConnection
from .node import Peer
from .proxy import Proxy
from .tcp import (
bootstrap, Socks4aBMConnection, Socks5BMConnection,
TCPConnection, TCPServer)
from udp import UDPSocket
from .udp import UDPSocket
logger = logging.getLogger('default')
def _ends_with(s, tail):
try:
return s.endswith(tail)
except:
return s.decode("utf-8", "replace").endswith(tail)
class BMConnectionPool(object):
"""Pool of all existing connections"""
# pylint: disable=too-many-instance-attributes
@ -78,7 +84,7 @@ class BMConnectionPool(object):
Shortcut for combined list of connections from
`inboundConnections` and `outboundConnections` dicts
"""
return self.inboundConnections.values() + self.outboundConnections.values()
return list(self.inboundConnections.values()) + list(self.outboundConnections.values())
def establishedConnections(self):
"""Shortcut for list of connections having fullyEstablished == True"""
@ -160,8 +166,8 @@ class BMConnectionPool(object):
@staticmethod
def getListeningIP():
"""What IP are we supposed to be listening on?"""
if config.safeGet(
"bitmessagesettings", "onionhostname", "").endswith(".onion"):
if _ends_with(config.safeGet(
"bitmessagesettings", "onionhostname", ""), ".onion"):
host = config.safeGet(
"bitmessagesettings", "onionbindip")
else:
@ -314,7 +320,7 @@ class BMConnectionPool(object):
continue
try:
if chosen.host.endswith(".onion") and Proxy.onion_proxy:
if _ends_with(chosen.host, ".onion") and Proxy.onion_proxy:
if onionsocksproxytype == "SOCKS5":
self.addConnection(Socks5BMConnection(chosen))
elif onionsocksproxytype == "SOCKS4a":
@ -381,14 +387,14 @@ class BMConnectionPool(object):
minTx -= 300 - 20
if i.lastTx < minTx:
if i.fullyEstablished:
i.append_write_buf(protocol.CreatePacket('ping'))
i.append_write_buf(protocol.CreatePacket(b'ping'))
else:
i.close_reason = "Timeout (%is)" % (
time.time() - i.lastTx)
i.set_state("close")
for i in (
self.connections()
+ self.listeningSockets.values() + self.udpSockets.values()
+ list(self.listeningSockets.values()) + list(self.udpSockets.values())
):
if not (i.accepting or i.connecting or i.connected):
reaper.append(i)

View File

@ -6,6 +6,8 @@ from collections import namedtuple
from random import choice, expovariate, sample
from threading import RLock
from time import time
import six
from binascii import hexlify
# randomise routes after 600 seconds
@ -64,7 +66,7 @@ class Dandelion: # pylint: disable=old-style-class
"""Add inventory vector to dandelion stem return status of dandelion enabled"""
assert self.enabled is not None
with self.lock:
self.hashMap[hashId] = Stem(
self.hashMap[bytes(hashId)] = Stem(
self.getNodeStem(source),
stream,
self.poissonTimeout())
@ -75,9 +77,10 @@ class Dandelion: # pylint: disable=old-style-class
include streams, we only learn this after receiving the object)
"""
with self.lock:
if hashId in self.hashMap:
self.hashMap[hashId] = Stem(
self.hashMap[hashId].child,
hashId_bytes = bytes(hashId)
if hashId_bytes in self.hashMap:
self.hashMap[hashId_bytes] = Stem(
self.hashMap[hashId_bytes].child,
stream,
self.poissonTimeout())
@ -86,20 +89,20 @@ class Dandelion: # pylint: disable=old-style-class
if logger.isEnabledFor(logging.DEBUG):
logger.debug(
'%s entering fluff mode due to %s.',
''.join('%02x' % ord(i) for i in hashId), reason)
hexlify(hashId), reason)
with self.lock:
try:
del self.hashMap[hashId]
del self.hashMap[bytes(hashId)]
except KeyError:
pass
def hasHash(self, hashId):
"""Is inventory vector in stem mode?"""
return hashId in self.hashMap
return bytes(hashId) in self.hashMap
def objectChildStem(self, hashId):
"""Child (i.e. next) node for an inventory vector during stem mode"""
return self.hashMap[hashId].child
return self.hashMap[bytes(hashId)].child
def maybeAddStem(self, connection, invQueue):
"""
@ -111,12 +114,12 @@ class Dandelion: # pylint: disable=old-style-class
with self.lock:
if len(self.stem) < MAX_STEMS:
self.stem.append(connection)
for k in (k for k, v in self.nodeMap.iteritems() if v is None):
for k in (k for k, v in six.iteritems(self.nodeMap) if v is None):
self.nodeMap[k] = connection
for k, v in {
k: v for k, v in self.hashMap.iteritems()
for k, v in six.iteritems({
k: v for k, v in six.iteritems(self.hashMap)
if v.child is None
}.iteritems():
}):
self.hashMap[k] = Stem(
connection, v.stream, self.poissonTimeout())
invQueue.put((v.stream, k, v.child))
@ -132,14 +135,14 @@ class Dandelion: # pylint: disable=old-style-class
self.stem.remove(connection)
# active mappings to pointing to the removed node
for k in (
k for k, v in self.nodeMap.iteritems()
k for k, v in six.iteritems(self.nodeMap)
if v == connection
):
self.nodeMap[k] = None
for k, v in {
k: v for k, v in self.hashMap.iteritems()
for k, v in six.iteritems({
k: v for k, v in six.iteritems(self.hashMap)
if v.child == connection
}.iteritems():
}):
self.hashMap[k] = Stem(
None, v.stream, self.poissonTimeout())
@ -180,7 +183,7 @@ class Dandelion: # pylint: disable=old-style-class
with self.lock:
deadline = time()
toDelete = [
[v.stream, k, v.child] for k, v in self.hashMap.iteritems()
[v.stream, k, v.child] for k, v in six.iteritems(self.hashMap)
if v.timeout < deadline
]
@ -199,10 +202,10 @@ class Dandelion: # pylint: disable=old-style-class
try:
# random two connections
self.stem = sample(
self.pool.outboundConnections.values(), MAX_STEMS)
sorted(self.pool.outboundConnections.values()), MAX_STEMS)
# not enough stems available
except ValueError:
self.stem = self.pool.outboundConnections.values()
self.stem = list(self.pool.outboundConnections.values())
self.nodeMap = {}
# hashMap stays to cater for pending stems
self.refresh = time() + REASSIGN_INTERVAL

View File

@ -4,12 +4,13 @@
import time
import random
import state
import six
import addresses
import protocol
import connectionpool
from network import connectionpool
from network import dandelion_ins
from objectracker import missingObjects
from threads import StoppableThread
from .objectracker import missingObjects
from .threads import StoppableThread
class DownloadThread(StoppableThread):
@ -29,7 +30,7 @@ class DownloadThread(StoppableThread):
deadline = time.time() - self.requestExpires
try:
toDelete = [
k for k, v in missingObjects.iteritems()
k for k, v in six.iteritems(missingObjects)
if v < deadline]
except RuntimeError:
pass
@ -68,11 +69,11 @@ class DownloadThread(StoppableThread):
continue
payload.extend(chunk)
chunkCount += 1
missingObjects[chunk] = now
missingObjects[bytes(chunk)] = now
if not chunkCount:
continue
payload[0:0] = addresses.encodeVarint(chunkCount)
i.append_write_buf(protocol.CreatePacket('getdata', payload))
i.append_write_buf(protocol.CreatePacket(b'getdata', payload))
self.logger.debug(
'%s:%i Requesting %i objects',
i.destination.host, i.destination.port, chunkCount)

View File

@ -1,10 +1,10 @@
import socket
from advanceddispatcher import AdvancedDispatcher
import asyncore_pollchoose as asyncore
from proxy import ProxyError
from socks5 import Socks5Connection, Socks5Resolver
from socks4a import Socks4aConnection, Socks4aResolver
from .advanceddispatcher import AdvancedDispatcher
from network import asyncore_pollchoose as asyncore
from .proxy import ProxyError
from .socks5 import Socks5Connection, Socks5Resolver
from .socks4a import Socks4aConnection, Socks4aResolver
class HttpError(ProxyError):

View File

@ -1,16 +1,16 @@
"""
Thread to send inv annoucements
"""
import Queue
from six.moves import queue as Queue
import random
from time import time
import addresses
import protocol
import state
import connectionpool
from network import connectionpool
from network import dandelion_ins, invQueue
from threads import StoppableThread
from .threads import StoppableThread
def handleExpiredDandelion(expired):
@ -90,15 +90,15 @@ class InvThread(StoppableThread):
if fluffs:
random.shuffle(fluffs)
connection.append_write_buf(protocol.CreatePacket(
'inv',
b'inv',
addresses.encodeVarint(
len(fluffs)) + ''.join(fluffs)))
len(fluffs)) + b''.join(fluffs)))
if stems:
random.shuffle(stems)
connection.append_write_buf(protocol.CreatePacket(
'dinv',
b'dinv',
addresses.encodeVarint(
len(stems)) + ''.join(stems)))
len(stems)) + b''.join(stems)))
invQueue.iterate()
for _ in range(len(chunk)):

View File

@ -10,10 +10,8 @@ import os
import pickle # nosec B403
import threading
import time
try:
from collections.abc import Iterable
except ImportError:
from collections import Iterable
from six.moves.collections_abc import Iterable
import six
import state
from bmconfigparser import config
@ -54,8 +52,8 @@ def json_serialize_knownnodes(output):
Reorganize knownnodes dict and write it as JSON to output
"""
_serialized = []
for stream, peers in knownNodes.iteritems():
for peer, info in peers.iteritems():
for stream, peers in six.iteritems(knownNodes):
for peer, info in six.iteritems(peers):
info.update(rating=round(info.get('rating', 0), 2))
_serialized.append({
'stream': stream, 'peer': peer._asdict(), 'info': info
@ -87,7 +85,7 @@ def pickle_deserialize_old_knownnodes(source):
global knownNodes
knownNodes = pickle.load(source) # nosec B301
for stream in knownNodes.keys():
for node, params in knownNodes[stream].iteritems():
for node, params in six.iteritems(knownNodes[stream]):
if isinstance(params, (float, int)):
addKnownNode(stream, node, params)
@ -97,7 +95,7 @@ def saveKnownNodes(dirName=None):
if dirName is None:
dirName = state.appdata
with knownNodesLock:
with open(os.path.join(dirName, 'knownnodes.dat'), 'wb') as output:
with open(os.path.join(dirName, 'knownnodes.dat'), 'w') as output:
json_serialize_knownnodes(output)
@ -108,6 +106,12 @@ def addKnownNode(stream, peer, lastseen=None, is_self=False):
Returns True if added a new node.
"""
# pylint: disable=too-many-branches
if not isinstance(peer.host, str):
try:
peer = Peer(peer.host.decode("ascii"), peer.port)
except UnicodeDecodeError as err:
logger.warning("Invalid host: {}".format(peer.host.decode("ascii", "backslashreplace")))
return
if isinstance(stream, Iterable):
with knownNodesLock:
for s in stream:
@ -153,7 +157,7 @@ def createDefaultKnownNodes():
def readKnownNodes():
"""Load knownnodes from filesystem"""
try:
with open(state.appdata + 'knownnodes.dat', 'rb') as source:
with open(state.appdata + 'knownnodes.dat', 'r') as source:
with knownNodesLock:
try:
json_deserialize_knownnodes(source)

View File

@ -2,9 +2,9 @@
A thread to handle network concerns
"""
import network.asyncore_pollchoose as asyncore
import connectionpool
from network import connectionpool
from queues import excQueue
from threads import StoppableThread
from .threads import StoppableThread
class BMNetworkThread(StoppableThread):

View File

@ -3,8 +3,9 @@ Module for tracking objects
"""
import time
from threading import RLock
import six
import connectionpool
import network.connectionpool # use long name to address recursive import
from network import dandelion_ins
from randomtrackingdict import RandomTrackingDict
@ -75,32 +76,35 @@ class ObjectTracker(object):
with self.objectsNewToThemLock:
self.objectsNewToThem = {
k: v
for k, v in self.objectsNewToThem.iteritems()
for k, v in six.iteritems(self.objectsNewToThem)
if v >= deadline}
self.lastCleaned = time.time()
def hasObj(self, hashid):
"""Do we already have object?"""
hashid_bytes = bytes(hashid)
if haveBloom:
return hashid in self.invBloom
return hashid in self.objectsNewToMe
return hashid_bytes in self.invBloom
return hashid_bytes in self.objectsNewToMe
def handleReceivedInventory(self, hashId):
"""Handling received inventory"""
hashId_bytes = bytes(hashId)
if haveBloom:
self.invBloom.add(hashId)
self.invBloom.add(hashId_bytes)
try:
with self.objectsNewToThemLock:
del self.objectsNewToThem[hashId]
del self.objectsNewToThem[hashId_bytes]
except KeyError:
pass
if hashId not in missingObjects:
missingObjects[hashId] = time.time()
if hashId_bytes not in missingObjects:
missingObjects[hashId_bytes] = time.time()
self.objectsNewToMe[hashId] = True
def handleReceivedObject(self, streamNumber, hashid):
"""Handling received object"""
for i in connectionpool.pool.connections():
hashid_bytes = bytes(hashid)
for i in network.connectionpool.pool.connections():
if not i.fullyEstablished:
continue
try:
@ -110,7 +114,7 @@ class ObjectTracker(object):
not dandelion_ins.hasHash(hashid)
or dandelion_ins.objectChildStem(hashid) == i):
with i.objectsNewToThemLock:
i.objectsNewToThem[hashid] = time.time()
i.objectsNewToThem[hashid_bytes] = time.time()
# update stream number,
# which we didn't have when we just received the dinv
# also resets expiration of the stem mode
@ -119,7 +123,7 @@ class ObjectTracker(object):
if i == self:
try:
with i.objectsNewToThemLock:
del i.objectsNewToThem[hashid]
del i.objectsNewToThem[hashid_bytes]
except KeyError:
pass
self.objectsNewToMe.setLastObject()
@ -133,4 +137,4 @@ class ObjectTracker(object):
def addAddr(self, hashid):
"""WIP, should be moved to addrthread.py or removed"""
if haveBloom:
self.addrBloom.add(hashid)
self.addrBloom.add(bytes(hashid))

View File

@ -6,14 +6,20 @@ import logging
import socket
import time
import asyncore_pollchoose as asyncore
from advanceddispatcher import AdvancedDispatcher
from network import asyncore_pollchoose as asyncore
from .advanceddispatcher import AdvancedDispatcher
from bmconfigparser import config
from node import Peer
from .node import Peer
logger = logging.getLogger('default')
def _ends_with(s, tail):
try:
return s.endswith(tail)
except:
return s.decode("utf-8", "replace").endswith(tail)
class ProxyError(Exception):
"""Base proxy exception class"""
errorCodes = ("Unknown error",)
@ -125,7 +131,7 @@ class Proxy(AdvancedDispatcher):
self.auth = None
self.connect(
self.onion_proxy
if address.host.endswith(".onion") and self.onion_proxy else
if _ends_with(address.host, ".onion") and self.onion_proxy else
self.proxy
)

View File

@ -2,13 +2,13 @@
Process data incoming from network
"""
import errno
import Queue
from six.moves import queue as Queue
import socket
import connectionpool
from network import connectionpool
from network.advanceddispatcher import UnknownStateError
from network import receiveDataQueue
from threads import StoppableThread
from .threads import StoppableThread
class ReceiveQueueThread(StoppableThread):

View File

@ -5,8 +5,9 @@ SOCKS4a proxy module
import logging
import socket
import struct
import six
from proxy import GeneralProxyError, Proxy, ProxyError
from .proxy import GeneralProxyError, Proxy, ProxyError
logger = logging.getLogger('default')
@ -39,16 +40,16 @@ class Socks4a(Proxy):
def state_pre_connect(self):
"""Handle feedback from SOCKS4a while it is connecting on our behalf"""
# Get the response
if self.read_buf[0:1] != chr(0x00).encode():
if self.read_buf[0:1] != six.int2byte(0x00):
# bad data
self.close()
raise GeneralProxyError(1)
elif self.read_buf[1:2] != chr(0x5A).encode():
elif self.read_buf[1:2] != six.int2byte(0x5A):
# Connection failed
self.close()
if ord(self.read_buf[1:2]) in (91, 92, 93):
if six.byte2int(self.read_buf[1:2]) in (91, 92, 93):
# socks 4 error
raise Socks4aError(ord(self.read_buf[1:2]) - 90)
raise Socks4aError(six.byte2int(self.read_buf[1:2]) - 90)
else:
raise Socks4aError(4)
# Get the bound address/port
@ -102,9 +103,9 @@ class Socks4aConnection(Socks4a):
self.append_write_buf(self.ipaddr)
if self._auth:
self.append_write_buf(self._auth[0])
self.append_write_buf(chr(0x00).encode())
self.append_write_buf(six.int2byte(0x00))
if rmtrslv:
self.append_write_buf(self.destination[0] + chr(0x00).encode())
self.append_write_buf(self.destination[0].encode("utf-8", "replace") + six.int2byte(0x00))
self.set_state("pre_connect", length=0, expectBytes=8)
return True
@ -132,8 +133,8 @@ class Socks4aResolver(Socks4a):
self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01))
if self._auth:
self.append_write_buf(self._auth[0])
self.append_write_buf(chr(0x00).encode())
self.append_write_buf(self.host + chr(0x00).encode())
self.append_write_buf(six.int2byte(0x00))
self.append_write_buf(self.host + six.int2byte(0x00))
self.set_state("pre_connect", length=0, expectBytes=8)
return True

View File

@ -6,9 +6,10 @@ SOCKS5 proxy module
import logging
import socket
import struct
import six
from node import Peer
from proxy import GeneralProxyError, Proxy, ProxyError
from .node import Peer
from .proxy import GeneralProxyError, Proxy, ProxyError
logger = logging.getLogger('default')
@ -97,20 +98,20 @@ class Socks5(Proxy):
def state_pre_connect(self):
"""Handle feedback from socks5 while it is connecting on our behalf."""
# Get the response
if self.read_buf[0:1] != chr(0x05).encode():
if self.read_buf[0:1] != six.int2byte(0x05):
self.close()
raise GeneralProxyError(1)
elif self.read_buf[1:2] != chr(0x00).encode():
elif self.read_buf[1:2] != six.int2byte(0x00):
# Connection failed
self.close()
if ord(self.read_buf[1:2]) <= 8:
raise Socks5Error(ord(self.read_buf[1:2]))
if six.byte2int(self.read_buf[1:2]) <= 8:
raise Socks5Error(six.byte2int(self.read_buf[1:2]))
else:
raise Socks5Error(9)
# Get the bound address/port
elif self.read_buf[3:4] == chr(0x01).encode():
elif self.read_buf[3:4] == six.int2byte(0x01):
self.set_state("proxy_addr_1", length=4, expectBytes=4)
elif self.read_buf[3:4] == chr(0x03).encode():
elif self.read_buf[3:4] == six.int2byte(0x03):
self.set_state("proxy_addr_2_1", length=4, expectBytes=1)
else:
self.close()
@ -129,7 +130,7 @@ class Socks5(Proxy):
(e.g. IPv6, onion, ...). This is part 1 which retrieves the
length of the data.
"""
self.address_length = ord(self.read_buf[0:1])
self.address_length = six.byte2int(self.read_buf[0:1])
self.set_state(
"proxy_addr_2_2", length=1, expectBytes=self.address_length)
return True
@ -171,19 +172,19 @@ class Socks5Connection(Socks5):
# use the IPv4 address request even if remote resolving was specified.
try:
self.ipaddr = socket.inet_aton(self.destination[0])
self.append_write_buf(chr(0x01).encode() + self.ipaddr)
self.append_write_buf(six.int2byte(0x01) + self.ipaddr)
except socket.error: # may be IPv6!
# Well it's not an IP number, so it's probably a DNS name.
if self._remote_dns:
# Resolve remotely
self.ipaddr = None
self.append_write_buf(chr(0x03).encode() + chr(
len(self.destination[0])).encode() + self.destination[0])
self.append_write_buf(six.int2byte(0x03) + six.int2byte(
len(self.destination[0])) + self.destination[0].encode("utf-8", "replace"))
else:
# Resolve locally
self.ipaddr = socket.inet_aton(
socket.gethostbyname(self.destination[0]))
self.append_write_buf(chr(0x01).encode() + self.ipaddr)
self.append_write_buf(six.int2byte(0x01) + self.ipaddr)
self.append_write_buf(struct.pack(">H", self.destination[1]))
self.set_state("pre_connect", length=0, expectBytes=4)
return True
@ -208,8 +209,8 @@ class Socks5Resolver(Socks5):
"""Perform resolving"""
# Now we can request the actual connection
self.append_write_buf(struct.pack('BBB', 0x05, 0xF0, 0x00))
self.append_write_buf(chr(0x03).encode() + chr(
len(self.host)).encode() + str(self.host))
self.append_write_buf(six.int2byte(0x03) + six.int2byte(
len(self.host)) + bytes(self.host))
self.append_write_buf(struct.pack(">H", self.port))
self.set_state("pre_connect", length=0, expectBytes=4)
return True

View File

@ -3,9 +3,9 @@ Network statistics
"""
import time
import asyncore_pollchoose as asyncore
import connectionpool
from objectracker import missingObjects
from network import asyncore_pollchoose as asyncore
from network import connectionpool
from .objectracker import missingObjects
lastReceivedTimestamp = time.time()

View File

@ -8,28 +8,29 @@ import math
import random
import socket
import time
import six
# magic imports!
import addresses
import l10n
import protocol
import state
import connectionpool
import network.connectionpool # use long name to address recursive import
from bmconfigparser import config
from highlevelcrypto import randomBytes
from network import dandelion_ins, invQueue, receiveDataQueue
from queues import UISignalQueue
from tr import _translate
import asyncore_pollchoose as asyncore
import knownnodes
from network import asyncore_pollchoose as asyncore
from network import knownnodes
from network.advanceddispatcher import AdvancedDispatcher
from network.bmproto import BMProto
from network.objectracker import ObjectTracker
from network.socks4a import Socks4aConnection
from network.socks5 import Socks5Connection
from network.tls import TLSDispatcher
from node import Peer
from .node import Peer
logger = logging.getLogger('default')
@ -39,6 +40,12 @@ maximumAgeOfNodesThatIAdvertiseToOthers = 10800 #: Equals three hours
maximumTimeOffsetWrongCount = 3 #: Connections with wrong time offset
def _ends_with(s, tail):
try:
return s.endswith(tail)
except:
return s.decode("utf-8", "replace").endswith(tail)
class TCPConnection(BMProto, TLSDispatcher):
# pylint: disable=too-many-instance-attributes
"""
@ -138,9 +145,9 @@ class TCPConnection(BMProto, TLSDispatcher):
'updateStatusBar',
_translate(
"MainWindow",
"The time on your computer, %1, may be wrong. "
"The time on your computer, {0}, may be wrong. "
"Please verify your settings."
).arg(l10n.formatTimestamp())))
).format(l10n.formatTimestamp())))
def state_connection_fully_established(self):
"""
@ -191,10 +198,10 @@ class TCPConnection(BMProto, TLSDispatcher):
# only if more recent than 3 hours
# and having positive or neutral rating
filtered = [
(k, v) for k, v in nodes.iteritems()
(k, v) for k, v in six.iteritems(nodes)
if v["lastseen"] > int(time.time())
- maximumAgeOfNodesThatIAdvertiseToOthers
and v["rating"] >= 0 and not k.host.endswith('.onion')
and v["rating"] >= 0 and not _ends_with(k.host, '.onion')
]
# sent 250 only if the remote isn't interested in it
elemCount = min(
@ -220,7 +227,7 @@ class TCPConnection(BMProto, TLSDispatcher):
'Sending huge inv message with %i objects to just this'
' one peer', objectCount)
self.append_write_buf(protocol.CreatePacket(
'inv', addresses.encodeVarint(objectCount) + payload))
b'inv', addresses.encodeVarint(objectCount) + payload))
# Select all hashes for objects in this stream.
bigInvList = {}
@ -267,7 +274,7 @@ class TCPConnection(BMProto, TLSDispatcher):
self.append_write_buf(
protocol.assembleVersionMessage(
self.destination.host, self.destination.port,
connectionpool.pool.streams, dandelion_ins.enabled,
network.connectionpool.pool.streams, dandelion_ins.enabled,
False, nodeid=self.nodeid))
self.connectedAt = time.time()
receiveDataQueue.put(self.destination)
@ -318,7 +325,7 @@ class Socks5BMConnection(Socks5Connection, TCPConnection):
self.append_write_buf(
protocol.assembleVersionMessage(
self.destination.host, self.destination.port,
connectionpool.pool.streams, dandelion_ins.enabled,
network.connectionpool.pool.streams, dandelion_ins.enabled,
False, nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size)
return True
@ -342,7 +349,7 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection):
self.append_write_buf(
protocol.assembleVersionMessage(
self.destination.host, self.destination.port,
connectionpool.pool.streams, dandelion_ins.enabled,
network.connectionpool.pool.streams, dandelion_ins.enabled,
False, nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size)
return True
@ -430,7 +437,7 @@ class TCPServer(AdvancedDispatcher):
state.ownAddresses[Peer(*sock.getsockname())] = True
if (
len(connectionpool.pool)
len(network.connectionpool.pool)
> config.safeGetInt(
'bitmessagesettings', 'maxtotalconnections')
+ config.safeGetInt(
@ -442,7 +449,7 @@ class TCPServer(AdvancedDispatcher):
sock.close()
return
try:
connectionpool.pool.addConnection(
network.connectionpool.pool.addConnection(
TCPConnection(sock=sock))
except socket.error:
pass

View File

@ -6,6 +6,7 @@ import os
import socket
import ssl
import sys
import six
import network.asyncore_pollchoose as asyncore
import paths
@ -34,7 +35,7 @@ else:
# ciphers
if (
ssl.OPENSSL_VERSION_NUMBER >= 0x10100000
and not ssl.OPENSSL_VERSION.startswith(b"LibreSSL")
and not ssl.OPENSSL_VERSION.startswith("LibreSSL")
):
sslProtocolCiphers = "AECDH-AES256-SHA@SECLEVEL=0"
else:
@ -58,14 +59,29 @@ class TLSDispatcher(AdvancedDispatcher):
self.tlsDone = False
self.tlsVersion = "N/A"
self.isSSL = False
if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
self.tlsPrepared = False
def state_tls_init(self):
"""Prepare sockets for TLS handshake"""
self.isSSL = True
self.tlsStarted = True
if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
self.want_read = self.want_write = True
self.set_state("tls_handshake")
return False
return self.do_tls_init()
def do_tls_init(self):
# Once the connection has been established,
# it's safe to wrap the socket.
if sys.version_info >= (2, 7, 9):
if ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER
if self.server_side else ssl.PROTOCOL_TLS_CLIENT)
else:
context = ssl.create_default_context(
purpose=ssl.Purpose.SERVER_AUTH
if self.server_side else ssl.Purpose.CLIENT_AUTH)
@ -74,6 +90,11 @@ class TLSDispatcher(AdvancedDispatcher):
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
# also exclude TLSv1 and TLSv1.1 in the future
if ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
context.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 |\
ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE |\
ssl.OP_CIPHER_SERVER_PREFERENCE | ssl.OP_NO_TLSv1_3
else:
context.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 |\
ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE |\
ssl.OP_CIPHER_SERVER_PREFERENCE
@ -88,6 +109,9 @@ class TLSDispatcher(AdvancedDispatcher):
ciphers=self.ciphers, do_handshake_on_connect=False)
self.sslSocket.setblocking(0)
self.want_read = self.want_write = True
if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
self.tlsPrepared = True
else:
self.set_state("tls_handshake")
return False
@ -114,6 +138,8 @@ class TLSDispatcher(AdvancedDispatcher):
# during TLS handshake, and after flushing write buffer,
# return status of last handshake attempt
if self.tlsStarted and not self.tlsDone and not self.write_buf:
# with OpenSSL 3, excessive logs are spitted
if ssl.OPENSSL_VERSION_NUMBER < 0x30000000:
logger.debug('tls readable, %r', self.want_read)
return self.want_read
# prior to TLS handshake,
@ -134,6 +160,10 @@ class TLSDispatcher(AdvancedDispatcher):
try:
# wait for write buffer flush
if self.tlsStarted and not self.tlsDone and not self.write_buf:
if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
if not self.tlsPrepared:
self.do_tls_init()
return
self.tls_handshake()
else:
AdvancedDispatcher.handle_read(self)
@ -156,6 +186,10 @@ class TLSDispatcher(AdvancedDispatcher):
try:
# wait for write buffer flush
if self.tlsStarted and not self.tlsDone and not self.write_buf:
if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
if not self.tlsPrepared:
self.do_tls_init()
return
self.tls_handshake()
else:
AdvancedDispatcher.handle_write(self)

View File

@ -8,12 +8,12 @@ import time
# magic imports!
import protocol
import state
import connectionpool
import network.connectionpool # use long name to address recursive import
from network import receiveDataQueue
from bmproto import BMProto
from node import Peer
from objectracker import ObjectTracker
from .bmproto import BMProto
from .node import Peer
from .objectracker import ObjectTracker
logger = logging.getLogger('default')
@ -81,8 +81,8 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
return True
remoteport = False
for seenTime, stream, _, ip, port in addresses:
decodedIP = protocol.checkIPAddress(str(ip))
if stream not in connectionpool.pool.streams:
decodedIP = protocol.checkIPAddress(ip)
if stream not in network.connectionpool.pool.streams:
continue
if (seenTime < time.time() - protocol.MAX_TIME_OFFSET
or seenTime > time.time() + protocol.MAX_TIME_OFFSET):

View File

@ -6,10 +6,10 @@ import time
import random
import protocol
import state
import connectionpool
from network import connectionpool
from randomtrackingdict import RandomTrackingDict
from network import dandelion_ins
from threads import StoppableThread
from .threads import StoppableThread
class UploadThread(StoppableThread):
@ -50,7 +50,7 @@ class UploadThread(StoppableThread):
break
try:
payload.extend(protocol.CreatePacket(
'object', state.Inventory[chunk].payload))
b'object', state.Inventory[chunk].payload))
chunk_count += 1
except KeyError:
i.antiIntersectionDelay()

View File

@ -23,9 +23,9 @@ class IndicatorLibmessaging(object):
return
self._menu = {
'send': unicode(_translate('MainWindow', 'Send')),
'messages': unicode(_translate('MainWindow', 'Messages')),
'subscriptions': unicode(_translate('MainWindow', 'Subscriptions'))
'send': _translate('MainWindow', 'Send'),
'messages': _translate('MainWindow', 'Messages'),
'subscriptions': _translate('MainWindow', 'Subscriptions')
}
self.new_message_item = self.new_broadcast_item = None
@ -45,12 +45,11 @@ class IndicatorLibmessaging(object):
def show_unread(self, draw_attention=False):
"""
show the number of unread messages and subscriptions
Show the number of unread messages and subscriptions
on the messaging menu
"""
for source, count in zip(
('messages', 'subscriptions'),
self.form.getUnread()
('messages', 'subscriptions'), self.form.getUnread()
):
if count > 0:
if self.app.has_source(source):

View File

@ -3,10 +3,10 @@
A menu plugin showing QR-Code for bitmessage address in modal dialog.
"""
import urllib
from six.moves.urllib.parse import urlencode
import qrcode
from PyQt4 import QtCore, QtGui
from qtpy import QtGui, QtCore, QtWidgets
from pybitmessage.tr import _translate
@ -39,23 +39,23 @@ class Image(qrcode.image.base.BaseImage): # pylint: disable=abstract-method
QtCore.Qt.black)
class QRCodeDialog(QtGui.QDialog):
class QRCodeDialog(QtWidgets.QDialog):
"""The dialog"""
def __init__(self, parent):
super(QRCodeDialog, self).__init__(parent)
self.image = QtGui.QLabel(self)
self.label = QtGui.QLabel(self)
self.image = QtWidgets.QLabel(self)
self.label = QtWidgets.QLabel(self)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.label.setFont(font)
self.label.setAlignment(
QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
buttonBox = QtGui.QDialogButtonBox(self)
buttonBox = QtWidgets.QDialogButtonBox(self)
buttonBox.setOrientation(QtCore.Qt.Horizontal)
buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
layout = QtGui.QVBoxLayout(self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.image)
layout.addWidget(self.label)
layout.addWidget(buttonBox)
@ -72,7 +72,7 @@ class QRCodeDialog(QtGui.QDialog):
self.label.setText(text)
self.label.setToolTip(text)
self.label.setFixedWidth(pixmap.width())
self.setFixedSize(QtGui.QWidget.sizeHint(self))
self.setFixedSize(QtWidgets.QWidget.sizeHint(self))
def connect_plugin(form):
@ -93,7 +93,7 @@ def connect_plugin(form):
return
dialog.render(
'bitmessage:%s' % account.address + (
'?' + urllib.urlencode({'label': label.encode('utf-8')})
'?' + urlencode({'label': label.encode('utf-8')})
if label != account.address else '')
)
dialog.exec_()

Some files were not shown because too many files have changed in this diff Show More