Merge branch 'v0.6' into fabric_task_improvements

python3android
coffeedogs 4 years ago committed by GitHub
commit 1d29322c5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 57
      checkdeps.py
  2. 1
      requirements.txt
  3. 13
      setup.cfg
  4. 21
      setup.py
  5. 2
      src/bitmessagemain.py
  6. 2043
      src/bitmessageqt/__init__.py
  7. 82
      src/defaultKnownNodes.py
  8. 66
      src/helper_bootstrap.py
  9. 128
      src/knownnodes.py
  10. 341
      src/protocol.py
  11. 206
      src/pyelliptic/arithmetic.py

@ -1,4 +1,13 @@
"""Check dependendies and give recommendations about how to satisfy them"""
"""
Check dependendies and give recommendations about how to satisfy them
Limitations:
* Does not detect whether packages are already installed. Solving this requires writing more of a configuration
management system. Or we could switch to an existing one.
* Not fully PEP508 compliant. Not slightly. It makes bold assumptions about the simplicity of the contents of
EXTRAS_REQUIRE. This is fine because most developers do, too.
"""
from distutils.errors import CompileError
try:
@ -12,6 +21,29 @@ from importlib import import_module
import os
import sys
from setup import EXTRAS_REQUIRE
PROJECT_ROOT = os.path.abspath('..')
sys.path.insert(0, PROJECT_ROOT)
# OS-specific dependencies for optional components listed in EXTRAS_REQUIRE
EXTRAS_REQUIRE_DEPS = {
# The values from setup.EXTRAS_REQUIRE
'python_prctl': {
# The packages needed for this requirement, by OS
"OpenBSD": [""],
"FreeBSD": [""],
"Debian": ["libcap-dev"],
"Ubuntu": [""],
"Ubuntu 12": [""],
"openSUSE": [""],
"Fedora": [""],
"Guix": [""],
"Gentoo": [""],
},
}
PACKAGE_MANAGER = {
"OpenBSD": "pkg_add",
"FreeBSD": "pkg install",
@ -199,6 +231,7 @@ if not compiler:
if prereqs:
mandatory = list(x for x in prereqs if "optional" not in PACKAGES[x] or not PACKAGES[x]["optional"])
optional = list(x for x in prereqs if "optional" in PACKAGES[x] and PACKAGES[x]["optional"])
if mandatory:
print "Missing mandatory dependencies: %s" % (" ".join(mandatory))
if optional:
@ -206,6 +239,28 @@ if prereqs:
for package in optional:
print PACKAGES[package].get('description')
# Install the system dependencies of optional extras_require components
OPSYS = detectOS()
CMD = PACKAGE_MANAGER[OPSYS] if OPSYS in PACKAGE_MANAGER else 'UNKNOWN_INSTALLER'
for lhs, rhs in EXTRAS_REQUIRE.items():
if rhs and any([
EXTRAS_REQUIRE_DEPS[x][OPSYS]
for x in rhs
if x in EXTRAS_REQUIRE_DEPS
]):
rhs_cmd = ''.join([
CMD,
' ',
' '.join([
''. join([
xx for xx in EXTRAS_REQUIRE_DEPS[x][OPSYS]
])
for x in rhs
if x in EXTRAS_REQUIRE_DEPS
]),
])
print "Optional dependency `pip install .[{}]` would require `{}` to be run as root".format(lhs, rhs_cmd)
if (not compiler or prereqs) and detectOS() in PACKAGE_MANAGER:
print "You can install the missing dependencies by running, as root:"
if not compiler:

@ -1 +0,0 @@
python_prctl

@ -1,10 +1,17 @@
# Since there is overlap in the violations that the different tools check for, it makes sense to quiesce some warnings
# in some tools if those warnings in other tools are preferred. This avoids the need to add duplicate lint warnings.
[pycodestyle]
max-line-length = 119
[flake8]
max-line-length = 119
ignore = E722
ignore = E722,F841
# E722: pylint is preferred for bare-except
# F841: pylint is preferred for unused-variable
# pylint
# pylint honours the [MESSAGES CONTROL] section
[MESSAGES CONTROL]
disable=invalid-name,bare-except
disable=invalid-name,bare-except,broad-except
# invalid-name: needs fixing during a large, project-wide refactor
# bare-except,broad-except: Need fixing once thorough testing is easier

@ -1,13 +1,24 @@
#!/usr/bin/env python2.7
import os
import sys
import shutil
from setuptools import setup, Extension
from setuptools.command.install import install
from src.version import softwareVersion
EXTRAS_REQUIRE = {
'gir': ['pygobject'],
'notify2': ['notify2'],
'pyopencl': ['pyopencl'],
'prctl': ['python_prctl'], # Named threads
'qrcode': ['qrcode'],
'sound;platform_system=="Windows"': ['winsound']
}
class InstallCmd(install):
def run(self):
# prepare icons directories
@ -78,13 +89,7 @@ if __name__ == "__main__":
# TODO: add keywords
#keywords='',
install_requires=installRequires,
extras_require={
'gir': ['pygobject'],
'qrcode': ['qrcode'],
'pyopencl': ['pyopencl'],
'notify2': ['notify2'],
'sound;platform_system=="Windows"': ['winsound']
},
extras_require=EXTRAS_REQUIRE,
classifiers=[
"License :: OSI Approved :: MIT License"
"Operating System :: OS Independent",

@ -263,7 +263,7 @@ class Main:
'bitmessagesettings', 'sendoutgoingconnections'):
state.dandelion = 0
helper_bootstrap.knownNodes()
knownnodes.readKnownNodes()
# Not needed if objproc is disabled
if state.enableObjProc:

@ -1,63 +1,78 @@
from debug import logger
# pylint: disable=too-many-lines,broad-except,too-many-instance-attributes,global-statement,too-few-public-methods
# pylint: disable=too-many-statements,too-many-branches,attribute-defined-outside-init,too-many-arguments,no-member
# pylint: disable=unused-argument,no-self-use,too-many-locals,unused-variable,too-many-nested-blocks
# pylint: disable=too-many-return-statements,protected-access,super-init-not-called,non-parent-init-called
"""
Initialise the QT interface
"""
import hashlib
import locale
import os
import random
import string
import sys
import textwrap
import time
from datetime import datetime, timedelta
import debug
from debug import logger
try:
from PyQt4 import QtCore, QtGui
from PyQt4.QtNetwork import QLocalSocket, QLocalServer
except Exception as err:
logmsg = 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).'
except ImportError:
logmsg = (
'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can'
' download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for '
'\'PyQt Download\' (without quotes).'
)
logger.critical(logmsg, exc_info=True)
sys.exit()
from tr import _translate
from addresses import decodeAddress, addBMIfNotPresent
import shared
from bitmessageui import Ui_MainWindow
from bmconfigparser import BMConfigParser
from sqlite3 import register_adapter # pylint: disable=wrong-import-order
import defaults
from namecoin import namecoinConnection
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)
from settings import Ui_settingsDialog
import settingsmixin
import support
import locale
import time
import os
import hashlib
from pyelliptic.openssl import OpenSSL
import textwrap
import debug
import random
from sqlite3 import register_adapter
import string
from datetime import datetime, timedelta
from helper_ackPayload import genAckPayload
from helper_sql import sqlQuery, sqlExecute, sqlExecuteChunked, sqlStoredProcedure
import helper_search
import knownnodes
import l10n
import openclpow
from utils import str_broadcast_subscribers, avatarize
from account import (
getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount,
GatewayAccount, MailchuckAccount, AccountColor)
import dialogs
from helper_generic import powQueueSize
from network.stats import pendingDownload, pendingUpload
from uisignaler import UISignaler
import knownnodes
import paths
from proofofwork import getPowType
import queues
import shared
import shutdown
import state
from statusbar import BMStatusBar
import upnp
from bitmessageqt import sound, support, dialogs
from bitmessageqt.foldertree import (
AccountMixin, Ui_FolderWidget, Ui_AddressWidget, Ui_SubscriptionWidget, MessageList_AddressWidget,
MessageList_SubjectWidget, Ui_AddressBookWidgetItemLabel, Ui_AddressBookWidgetItemAddress,
)
from bitmessageqt.account import (
getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount, GatewayAccount, MailchuckAccount, AccountColor,
)
from bitmessageqt.bitmessageui import Ui_MainWindow, settingsmixin
from bitmessageqt.messageview import MessageView
from bitmessageqt.migrationwizard import Ui_MigrationWizard
from bitmessageqt.settings import Ui_settingsDialog
from bitmessageqt.utils import str_broadcast_subscribers, avatarize
from bitmessageqt.uisignaler import UISignaler
from bitmessageqt.statusbar import BMStatusBar
from addresses import decodeAddress, addBMIfNotPresent
from bmconfigparser import BMConfigParser
from namecoin import namecoinConnection
from helper_ackPayload import genAckPayload
from helper_generic import powQueueSize
from helper_sql import sqlQuery, sqlExecute, sqlExecuteChunked, sqlStoredProcedure
from network.stats import pendingDownload, pendingUpload
from network.asyncore_pollchoose import set_rates
import sound
from proofofwork import getPowType
from tr import _translate
try:
@ -66,8 +81,15 @@ except ImportError:
get_plugins = False
qmytranslator = None
qsystranslator = None
def change_translation(newlocale):
"""Change a translation to a new locale"""
global qmytranslator, qsystranslator
try:
if not qmytranslator.isEmpty():
QtGui.QApplication.removeTranslator(qmytranslator)
@ -80,15 +102,16 @@ def change_translation(newlocale):
pass
qmytranslator = QtCore.QTranslator()
translationpath = os.path.join (paths.codePath(), 'translations', 'bitmessage_' + newlocale)
translationpath = os.path.join(paths.codePath(), 'translations', 'bitmessage_' + newlocale)
qmytranslator.load(translationpath)
QtGui.QApplication.installTranslator(qmytranslator)
qsystranslator = QtCore.QTranslator()
if paths.frozen:
translationpath = os.path.join (paths.codePath(), 'translations', 'qt_' + newlocale)
translationpath = os.path.join(paths.codePath(), 'translations', 'qt_' + newlocale)
else:
translationpath = os.path.join (str(QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)), 'qt_' + newlocale)
translationpath = os.path.join(str(QtCore.QLibraryInfo.location(
QtCore.QLibraryInfo.TranslationsPath)), 'qt_' + newlocale)
qsystranslator.load(translationpath)
QtGui.QApplication.installTranslator(qsystranslator)
@ -109,7 +132,8 @@ def change_translation(newlocale):
logger.error("Failed to set locale to %s", lang, exc_info=True)
class MyForm(settingsmixin.SMainWindow):
class MyForm(settingsmixin.SMainWindow): # pylint: disable=too-many-public-methods
"""TBC"""
# the last time that a message arrival sound was played
lastSoundTime = datetime.now() - timedelta(days=1)
@ -121,6 +145,8 @@ class MyForm(settingsmixin.SMainWindow):
REPLY_TYPE_CHAN = 1
def init_file_menu(self):
"""Initialise the file menu"""
QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL(
"triggered()"), self.quit)
QtCore.QObject.connect(self.ui.actionNetworkSwitch, QtCore.SIGNAL(
@ -135,9 +161,10 @@ class MyForm(settingsmixin.SMainWindow):
QtCore.SIGNAL(
"triggered()"),
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.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(
@ -162,7 +189,8 @@ class MyForm(settingsmixin.SMainWindow):
"triggered()"), self.click_actionHelp)
def init_inbox_popup_menu(self, connectSignal=True):
# Popup menu for the Inbox tab
"""Popup menu for the Inbox tab"""
self.ui.inboxContextMenuToolbar = QtGui.QToolBar()
# Actions
self.actionReply = self.ui.inboxContextMenuToolbar.addAction(_translate(
@ -199,24 +227,28 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.tableWidgetInbox.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.tableWidgetInbox, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuInbox)
self.connect(
self.ui.tableWidgetInbox,
QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'),
self.on_context_menuInbox)
self.ui.tableWidgetInboxSubscriptions.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.tableWidgetInboxSubscriptions, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuInbox)
self.connect(
self.ui.tableWidgetInboxSubscriptions,
QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'),
self.on_context_menuInbox)
self.ui.tableWidgetInboxChans.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.tableWidgetInboxChans, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuInbox)
self.connect(
self.ui.tableWidgetInboxChans,
QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'),
self.on_context_menuInbox)
def init_identities_popup_menu(self, connectSignal=True):
# Popup menu for the Your Identities tab
"""Popup menu for the Your Identities tab"""
self.ui.addressContextMenuToolbarYourIdentities = QtGui.QToolBar()
# Actions
self.actionNewYourIdentities = self.ui.addressContextMenuToolbarYourIdentities.addAction(_translate(
@ -251,9 +283,10 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.treeWidgetYourIdentities.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.treeWidgetYourIdentities, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuYourIdentities)
self.connect(
self.ui.treeWidgetYourIdentities,
QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'),
self.on_context_menuYourIdentities)
# load all gui.menu plugins with prefix 'address'
self.menu_plugins = {'address': []}
@ -268,7 +301,8 @@ class MyForm(settingsmixin.SMainWindow):
))
def init_chan_popup_menu(self, connectSignal=True):
# Popup menu for the Channels tab
"""Popup menu for the Channels tab"""
self.ui.addressContextMenuToolbar = QtGui.QToolBar()
# Actions
self.actionNew = self.ui.addressContextMenuToolbar.addAction(_translate(
@ -298,12 +332,14 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.treeWidgetChans.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.treeWidgetChans, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuChan)
self.connect(
self.ui.treeWidgetChans,
QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'),
self.on_context_menuChan)
def init_addressbook_popup_menu(self, connectSignal=True):
# Popup menu for the Address Book page
"""Popup menu for the Address Book page"""
self.ui.addressBookContextMenuToolbar = QtGui.QToolBar()
# Actions
self.actionAddressBookSend = self.ui.addressBookContextMenuToolbar.addAction(
@ -335,12 +371,14 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.tableWidgetAddressBook.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.tableWidgetAddressBook, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuAddressBook)
self.connect(
self.ui.tableWidgetAddressBook,
QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'),
self.on_context_menuAddressBook)
def init_subscriptions_popup_menu(self, connectSignal=True):
# Popup menu for the Subscriptions page
"""Popup menu for the Subscriptions page"""
self.ui.subscriptionsContextMenuToolbar = QtGui.QToolBar()
# Actions
self.actionsubscriptionsNew = self.ui.subscriptionsContextMenuToolbar.addAction(
@ -363,12 +401,14 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.treeWidgetSubscriptions.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
self.connect(self.ui.treeWidgetSubscriptions, QtCore.SIGNAL(
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuSubscriptions)
self.connect(
self.ui.treeWidgetSubscriptions,
QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'),
self.on_context_menuSubscriptions)
def init_sent_popup_menu(self, connectSignal=True):
# Popup menu for the Sent page
"""Popup menu for the Sent page"""
self.ui.sentContextMenuToolbar = QtGui.QToolBar()
# Actions
self.actionTrashSentMessage = self.ui.sentContextMenuToolbar.addAction(
@ -381,11 +421,10 @@ class MyForm(settingsmixin.SMainWindow):
self.actionForceSend = self.ui.sentContextMenuToolbar.addAction(
_translate(
"MainWindow", "Force send"), self.on_action_ForceSend)
# self.popMenuSent = QtGui.QMenu( self )
# self.popMenuSent.addAction( self.actionSentClipboard )
# self.popMenuSent.addAction( self.actionTrashSentMessage )
def rerenderTabTreeSubscriptions(self):
"""TBC"""
treeWidget = self.ui.treeWidgetSubscriptions
folders = Ui_FolderWidget.folderWeight.keys()
folders.remove("new")
@ -395,17 +434,16 @@ class MyForm(settingsmixin.SMainWindow):
treeWidget.header().setSortIndicator(
0, QtCore.Qt.AscendingOrder)
# init dictionary
db = getSortedSubscriptions(True)
for address in db:
for folder in folders:
if not folder in db[address]:
if folder not in db[address]:
db[address][folder] = {}
if treeWidget.isSortingEnabled():
treeWidget.setSortingEnabled(False)
widgets = {}
i = 0
while i < treeWidget.topLevelItemCount():
widget = treeWidget.topLevelItem(i)
@ -413,8 +451,8 @@ class MyForm(settingsmixin.SMainWindow):
toAddress = widget.address
else:
toAddress = None
if not toAddress in db:
if toAddress not in db:
treeWidget.takeTopLevelItem(i)
# no increment
continue
@ -433,7 +471,7 @@ class MyForm(settingsmixin.SMainWindow):
j += 1
# add missing folders
if len(db[toAddress]) > 0:
if db[toAddress]:
j = 0
for f, c in db[toAddress].iteritems():
try:
@ -444,10 +482,14 @@ class MyForm(settingsmixin.SMainWindow):
widget.setUnreadCount(unread)
db.pop(toAddress, None)
i += 1
i = 0
for toAddress in db:
widget = Ui_SubscriptionWidget(treeWidget, i, toAddress, db[toAddress]["inbox"]['count'], db[toAddress]["inbox"]['label'], db[toAddress]["inbox"]['enabled'])
widget = Ui_SubscriptionWidget(
treeWidget, i, toAddress,
db[toAddress]["inbox"]['count'],
db[toAddress]["inbox"]['label'],
db[toAddress]["inbox"]['enabled'])
j = 0
unread = 0
for folder in folders:
@ -459,23 +501,28 @@ class MyForm(settingsmixin.SMainWindow):
j += 1
widget.setUnreadCount(unread)
i += 1
treeWidget.setSortingEnabled(True)
treeWidget.setSortingEnabled(True)
def rerenderTabTreeMessages(self):
"""TBC"""
self.rerenderTabTree('messages')
def rerenderTabTreeChans(self):
"""TBC"""
self.rerenderTabTree('chan')
def rerenderTabTree(self, tab):
"""TBC"""
if tab == 'messages':
treeWidget = self.ui.treeWidgetYourIdentities
elif tab == 'chan':
treeWidget = self.ui.treeWidgetChans
folders = Ui_FolderWidget.folderWeight.keys()
# sort ascending when creating
if treeWidget.topLevelItemCount() == 0:
treeWidget.header().setSortIndicator(
@ -483,14 +530,12 @@ class MyForm(settingsmixin.SMainWindow):
# init dictionary
db = {}
enabled = {}
for toAddress in getSortedAccounts():
isEnabled = BMConfigParser().getboolean(
toAddress, 'enabled')
isChan = BMConfigParser().safeGetBoolean(
toAddress, 'chan')
isMaillinglist = BMConfigParser().safeGetBoolean(
toAddress, 'mailinglist')
if treeWidget == self.ui.treeWidgetYourIdentities:
if isChan:
@ -502,12 +547,13 @@ class MyForm(settingsmixin.SMainWindow):
db[toAddress] = {}
for folder in folders:
db[toAddress][folder] = 0
enabled[toAddress] = isEnabled
# get number of (unread) messages
total = 0
queryreturn = sqlQuery('SELECT toaddress, folder, count(msgid) as cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder')
queryreturn = sqlQuery(
'SELECT toaddress, folder, count(msgid) as cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder')
for row in queryreturn:
toaddress, folder, cnt = row
total += cnt
@ -520,11 +566,10 @@ class MyForm(settingsmixin.SMainWindow):
db[None]["sent"] = 0
db[None]["trash"] = 0
enabled[None] = True
if treeWidget.isSortingEnabled():
treeWidget.setSortingEnabled(False)
widgets = {}
i = 0
while i < treeWidget.topLevelItemCount():
widget = treeWidget.topLevelItem(i)
@ -532,8 +577,8 @@ class MyForm(settingsmixin.SMainWindow):
toAddress = widget.address
else:
toAddress = None
if not toAddress in db:
if toAddress not in db:
treeWidget.takeTopLevelItem(i)
# no increment
continue
@ -553,7 +598,7 @@ class MyForm(settingsmixin.SMainWindow):
j += 1
# add missing folders
if len(db[toAddress]) > 0:
if db[toAddress]:
j = 0
for f, c in db[toAddress].iteritems():
if toAddress is not None and tab == 'messages' and folder == "new":
@ -565,7 +610,7 @@ class MyForm(settingsmixin.SMainWindow):
widget.setUnreadCount(unread)
db.pop(toAddress, None)
i += 1
i = 0
for toAddress in db:
widget = Ui_AddressWidget(treeWidget, i, toAddress, db[toAddress]["inbox"], enabled[toAddress])
@ -580,10 +625,12 @@ class MyForm(settingsmixin.SMainWindow):
j += 1
widget.setUnreadCount(unread)
i += 1
treeWidget.setSortingEnabled(True)
def __init__(self, parent=None):
"""TBC"""
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
@ -591,12 +638,14 @@ class MyForm(settingsmixin.SMainWindow):
# Ask the user if we may delete their old version 1 addresses if they
# have any.
for addressInKeysFile in getSortedAccounts():
status, addressVersionNumber, streamNumber, hash = decodeAddress(
status, addressVersionNumber, streamNumber, addressHash = decodeAddress(
addressInKeysFile)
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)
"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:
@ -606,11 +655,13 @@ class MyForm(settingsmixin.SMainWindow):
# Configure Bitmessage to start on startup (or remove the
# configuration) based on the setting in the keys.dat file
if 'win32' in sys.platform or 'win64' in sys.platform:
# Auto-startup for Windows
RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
self.settings = QtCore.QSettings(RUN_PATH, QtCore.QSettings.NativeFormat)
self.settings.remove(
"PyBitmessage") # In case the user moves the program and the registry entry is no longer valid, this will delete the old registry entry.
# In case the user moves the program and the registry entry is no longer
# valid, this will delete the old registry entry.
self.settings.remove("PyBitmessage")
if BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'):
self.settings.setValue("PyBitmessage", sys.argv[0])
elif 'darwin' in sys.platform:
@ -622,13 +673,13 @@ class MyForm(settingsmixin.SMainWindow):
# e.g. for editing labels
self.recurDepth = 0
# switch back to this when replying
self.replyFromTab = None
# so that quit won't loop
self.quitAccepted = False
self.init_file_menu()
self.init_inbox_popup_menu()
self.init_identities_popup_menu()
@ -724,13 +775,13 @@ class MyForm(settingsmixin.SMainWindow):
self.unreadCount = 0
# Set the icon sizes for the identicons
identicon_size = 3*7
identicon_size = 3 * 7
self.ui.tableWidgetInbox.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.ui.treeWidgetChans.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.ui.treeWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.ui.treeWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size))
self.UISignalThread = UISignaler.get()
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.writeNewAddressToTable)
@ -740,10 +791,15 @@ class MyForm(settingsmixin.SMainWindow):
"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.displayNewInboxMessage)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.displayNewSentMessage)
QtCore.QObject.connect(
self.UISignalThread, QtCore.SIGNAL(
"displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
self.displayNewInboxMessage)
QtCore.QObject.connect(
self.UISignalThread, QtCore.SIGNAL(
("displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,"
"PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)")),
self.displayNewSentMessage)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"setStatusIcon(PyQt_PyObject)"), self.setStatusIcon)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
@ -784,16 +840,16 @@ class MyForm(settingsmixin.SMainWindow):
self.rerenderComboBoxSendFrom()
self.rerenderComboBoxSendFromBroadcast()
# Put the TTL slider in the correct spot
TTL = BMConfigParser().getint('bitmessagesettings', 'ttl')
if TTL < 3600: # an hour
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((TTL - 3600) ** (1 / 3.199))
self.updateHumanFriendlyTTLDescription(TTL)
QtCore.QObject.connect(self.ui.horizontalSliderTTL, QtCore.SIGNAL(
"valueChanged(int)"), self.updateTTL)
@ -805,20 +861,24 @@ class MyForm(settingsmixin.SMainWindow):
# Hide the 'Fetch Namecoin ID' button if we can't.
if BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'dontconnect'
) or self.namecoin.test()[0] == 'failed':
) or self.namecoin.test()[0] == 'failed':
logger.warning(
'There was a problem testing for a Namecoin daemon. Hiding the'
' Fetch Namecoin ID button')
self.ui.pushButtonFetchNamecoinID.hide()
def updateTTL(self, sliderPosition):
"""TBC"""
TTL = int(sliderPosition ** 3.199 + 3600)
self.updateHumanFriendlyTTLDescription(TTL)
BMConfigParser().set('bitmessagesettings', 'ttl', str(TTL))
BMConfigParser().save()
def updateHumanFriendlyTTLDescription(self, TTL):
numberOfHours = int(round(TTL / (60*60)))
"""TBC"""
numberOfHours = int(round(TTL / (60 * 60)))
font = QtGui.QFont()
stylesheet = ""
@ -827,19 +887,28 @@ class MyForm(settingsmixin.SMainWindow):
_translate("MainWindow", "%n hour(s)", None, QtCore.QCoreApplication.CodecForTr, numberOfHours) +
", " +
_translate("MainWindow", "not recommended for chans", None, QtCore.QCoreApplication.CodecForTr)
)
)
stylesheet = "QLabel { color : red; }"
font.setBold(True)
else:
numberOfDays = int(round(TTL / (24*60*60)))
self.ui.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "%n day(s)", None, QtCore.QCoreApplication.CodecForTr, numberOfDays))
numberOfDays = int(round(TTL / (24 * 60 * 60)))
self.ui.labelHumanFriendlyTTLDescription.setText(
_translate(
"MainWindow",
"%n day(s)",
None,
QtCore.QCoreApplication.CodecForTr,
numberOfDays))
font.setBold(False)
self.ui.labelHumanFriendlyTTLDescription.setStyleSheet(stylesheet)
self.ui.labelHumanFriendlyTTLDescription.setFont(font)
# Show or hide the application window after clicking an item within the
# tray icon or, on Windows, the try icon itself.
def appIndicatorShowOrHideWindow(self):
"""
Show or hide the application window after clicking an item within the tray icon or, on Windows,
the try icon itself.
"""
if not self.actionShow.isChecked():
self.hide()
else:
@ -849,16 +918,18 @@ class MyForm(settingsmixin.SMainWindow):
self.raise_()
self.activateWindow()
# show the application window
def appIndicatorShow(self):
"""show the application window"""
if self.actionShow is None:
return
if not self.actionShow.isChecked():
self.actionShow.setChecked(True)
self.appIndicatorShowOrHideWindow()
# unchecks the show item on the application indicator
def appIndicatorHide(self):
"""unchecks the show item on the application indicator"""
if self.actionShow is None:
return
if self.actionShow.isChecked():
@ -866,25 +937,16 @@ class MyForm(settingsmixin.SMainWindow):
self.appIndicatorShowOrHideWindow()
def appIndicatorSwitchQuietMode(self):
"""TBC"""
BMConfigParser().set(
'bitmessagesettings', 'showtraynotifications',
str(not self.actionQuiet.isChecked())
)
# application indicator show or hide
"""# application indicator show or hide
def appIndicatorShowBitmessage(self):
#if self.actionShow == None:
# return
print self.actionShow.isChecked()
if not self.actionShow.isChecked():
self.hide()
#self.setWindowState(self.windowState() & QtCore.Qt.WindowMinimized)
else:
self.appIndicatorShowOrHideWindow()"""
# Show the program window and select inbox tab
def appIndicatorInbox(self, item=None):
"""Show the program window and select inbox tab"""
self.appIndicatorShow()
# select inbox
self.ui.tabWidget.setCurrentIndex(
@ -900,22 +962,24 @@ class MyForm(settingsmixin.SMainWindow):
else:
self.ui.tableWidgetInbox.setCurrentCell(0, 0)
# Show the program window and select send tab
def appIndicatorSend(self):
"""Show the program window and select send tab"""
self.appIndicatorShow()
self.ui.tabWidget.setCurrentIndex(
self.ui.tabWidget.indexOf(self.ui.send)
)
# Show the program window and select subscriptions tab
def appIndicatorSubscribe(self):
"""Show the program window and select subscriptions tab"""
self.appIndicatorShow()
self.ui.tabWidget.setCurrentIndex(
self.ui.tabWidget.indexOf(self.ui.subscriptions)
)
# Show the program window and select channels tab
def appIndicatorChannel(self):
"""Show the program window and select channels tab"""
self.appIndicatorShow()
self.ui.tabWidget.setCurrentIndex(
self.ui.tabWidget.indexOf(self.ui.chans)
@ -961,9 +1025,12 @@ class MyForm(settingsmixin.SMainWindow):
for col in (0, 1, 2):
related.item(rrow, col).setUnread(not status)
def propagateUnreadCount(self, address = None, folder = "inbox", widget = None, type = 1):
def propagateUnreadCount(self, address=None, folder="inbox", widget=None, type_arg=1):
"""TBC"""
widgets = [self.ui.treeWidgetYourIdentities, self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans]
queryReturn = sqlQuery("SELECT toaddress, folder, COUNT(msgid) AS cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder")
queryReturn = sqlQuery(
"SELECT toaddress, folder, COUNT(msgid) AS cnt FROM inbox WHERE read = 0 GROUP BY toaddress, folder")
totalUnread = {}
normalUnread = {}
for row in queryReturn:
@ -975,12 +1042,15 @@ class MyForm(settingsmixin.SMainWindow):
totalUnread[row[1]] += row[2]
else:
totalUnread[row[1]] = row[2]
queryReturn = sqlQuery("SELECT fromaddress, folder, COUNT(msgid) AS cnt FROM inbox WHERE read = 0 AND toaddress = ? GROUP BY fromaddress, folder", str_broadcast_subscribers)
queryReturn = sqlQuery(
"SELECT fromaddress, folder, COUNT(msgid) AS cnt FROM inbox "
"WHERE read = 0 AND toaddress = ? GROUP BY fromaddress, folder",
str_broadcast_subscribers)
broadcastsUnread = {}
for row in queryReturn:
broadcastsUnread[row[0]] = {}
broadcastsUnread[row[0]][row[1]] = row[2]
for treeWidget in widgets:
root = treeWidget.invisibleRootItem()
for i in range(root.childCount()):
@ -1007,7 +1077,8 @@ class MyForm(settingsmixin.SMainWindow):
if addressItem.type == AccountMixin.ALL and folderName in totalUnread:
newCount = totalUnread[folderName]
elif addressItem.type == AccountMixin.SUBSCRIPTION:
if addressItem.address in broadcastsUnread and folderName in broadcastsUnread[addressItem.address]:
if addressItem.address in broadcastsUnread and folderName in broadcastsUnread[
addressItem.address]:
newCount = broadcastsUnread[addressItem.address][folderName]
elif addressItem.address in normalUnread and folderName in normalUnread[addressItem.address]:
newCount = normalUnread[addressItem.address][folderName]
@ -1015,16 +1086,20 @@ class MyForm(settingsmixin.SMainWindow):
folderItem.setUnreadCount(newCount)
def addMessageListItem(self, tableWidget, items):
"""TBC"""
sortingEnabled = tableWidget.isSortingEnabled()
if sortingEnabled:
tableWidget.setSortingEnabled(False)
tableWidget.insertRow(0)
for i in range(len(items)):
for i, _ in enumerate(items):
tableWidget.setItem(0, i, items[i])
if sortingEnabled:
tableWidget.setSortingEnabled(True)
def addMessageListItemSent(self, tableWidget, toAddress, fromAddress, subject, status, ackdata, lastactiontime):
"""TBC"""
acct = accountClass(fromAddress)
if acct is None:
acct = BMAccount(fromAddress)
@ -1063,14 +1138,19 @@ class MyForm(settingsmixin.SMainWindow):
statusText = _translate(
"MainWindow", "Doing work necessary to send broadcast.")
elif status == 'broadcastsent':
statusText = _translate("MainWindow", "Broadcast on %1").arg(
l10n.formatTimestamp(lastactiontime))
statusText = _translate(
"MainWindow", "Broadcast on %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'toodifficult':
statusText = _translate("MainWindow", "Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg(
l10n.formatTimestamp(lastactiontime))
statusText = _translate(
"MainWindow",
"Problem: The work demanded by the recipient is more difficult than you are willing to do. %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'badkey':
statusText = _translate("MainWindow", "Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1").arg(
l10n.formatTimestamp(lastactiontime))
statusText = _translate(
"MainWindow",
"Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1").arg(
l10n.formatTimestamp(lastactiontime))
elif status == 'forcepow':
statusText = _translate(
"MainWindow", "Forced difficulty override. Send should start soon.")
@ -1088,6 +1168,8 @@ class MyForm(settingsmixin.SMainWindow):
return acct
def addMessageListItemInbox(self, tableWidget, msgfolder, msgid, toAddress, fromAddress, subject, received, read):
"""TBC"""
font = QtGui.QFont()
font.setBold(True)
if toAddress == str_broadcast_subscribers:
@ -1099,9 +1181,9 @@ class MyForm(settingsmixin.SMainWindow):
if acct is None:
acct = BMAccount(fromAddress)
acct.parseMessage(toAddress, fromAddress, subject, "")
items = []
#to
# to
MessageList_AddressWidget(items, toAddress, unicode(acct.toLabel, 'utf-8'), not read)
# from
MessageList_AddressWidget(items, fromAddress, unicode(acct.fromLabel, 'utf-8'), not read)
@ -1120,8 +1202,9 @@ class MyForm(settingsmixin.SMainWindow):
self.addMessageListItem(tableWidget, items)
return acct
# Load Sent items from database
def loadSent(self, tableWidget, account, where="", what=""):
"""Load Sent items from database"""
if tableWidget == self.ui.tableWidgetInboxSubscriptions:
tableWidget.setColumnHidden(0, True)
tableWidget.setColumnHidden(1, False)
@ -1153,8 +1236,9 @@ class MyForm(settingsmixin.SMainWindow):
tableWidget.horizontalHeaderItem(3).setText(_translate("MainWindow", "Sent", None))
tableWidget.setUpdatesEnabled(True)
# Load messages from database file
def loadMessagelist(self, tableWidget, account, folder="inbox", where="", what="", unreadOnly = False):
def loadMessagelist(self, tableWidget, account, folder="inbox", where="", what="", unreadOnly=False):
"""Load messages from database file"""
if folder == 'sent':
self.loadSent(tableWidget, account, where, what)
return
@ -1175,10 +1259,11 @@ class MyForm(settingsmixin.SMainWindow):
tableWidget.setRowCount(0)
queryreturn = helper_search.search_sql(xAddress, account, folder, where, what, unreadOnly)
for row in queryreturn:
msgfolder, msgid, toAddress, fromAddress, subject, received, read = row
self.addMessageListItemInbox(tableWidget, msgfolder, msgid, toAddress, fromAddress, subject, received, read)
self.addMessageListItemInbox(tableWidget, msgfolder, msgid, toAddress,
fromAddress, subject, received, read)
tableWidget.horizontalHeader().setSortIndicator(
3, QtCore.Qt.DescendingOrder)
@ -1187,9 +1272,10 @@ class MyForm(settingsmixin.SMainWindow):
tableWidget.horizontalHeaderItem(3).setText(_translate("MainWindow", "Received", None))
tableWidget.setUpdatesEnabled(True)
# create application indicator
def appIndicatorInit(self, app):
self.initTrayIcon("can-icon-24px-red.png", app)
def appIndicatorInit(self, this_app):
"""create application indicator"""
self.initTrayIcon("can-icon-24px-red.png", this_app)
traySignal = "activated(QSystemTrayIcon::ActivationReason)"
QtCore.QObject.connect(self.tray, QtCore.SIGNAL(
traySignal), self.__icon_activated)
@ -1211,7 +1297,7 @@ class MyForm(settingsmixin.SMainWindow):
self.actionShow.setChecked(not BMConfigParser().getboolean(
'bitmessagesettings', 'startintray'))
self.actionShow.triggered.connect(self.appIndicatorShowOrHideWindow)
if not sys.platform[0:3] == 'win':
if sys.platform[0:3] != 'win':
m.addAction(self.actionShow)
# quiet mode
@ -1252,8 +1338,9 @@ class MyForm(settingsmixin.SMainWindow):
self.tray.setContextMenu(m)
self.tray.show()
# returns the number of unread messages and subscriptions
def getUnread(self):
"""returns the number of unread messages and subscriptions"""
counters = [0, 0]
queryreturn = sqlQuery('''
@ -1268,12 +1355,15 @@ class MyForm(settingsmixin.SMainWindow):
return counters
# play a sound
def playSound(self, category, label):
def playSound(self, category, label): # pylint: disable=inconsistent-return-statements
"""play a sound"""
# filename of the sound to be played
soundFilename = None
def _choose_ext(basename):
def _choose_ext(basename): # pylint: disable=inconsistent-return-statements
"""TBC"""
for ext in sound.extensions:
if os.path.isfile(os.extsep.join([basename, ext])):
return os.extsep + ext
@ -1295,24 +1385,23 @@ class MyForm(settingsmixin.SMainWindow):
# elapsed time since the last sound was played
dt = datetime.now() - self.lastSoundTime
# suppress sounds which are more frequent than the threshold
if dt.total_seconds() < self.maxSoundFrequencySec:
return
# the sound is for an address which exists in the address book
if category is sound.SOUND_KNOWN:
soundFilename = state.appdata + 'sounds/known'
# the sound is for an unknown address
elif category is sound.SOUND_UNKNOWN:
soundFilename = state.appdata + 'sounds/unknown'
# initial connection sound
elif category is sound.SOUND_CONNECTED:
soundFilename = state.appdata + 'sounds/connected'
# disconnected sound
elif category is sound.SOUND_DISCONNECTED:
soundFilename = state.appdata + 'sounds/disconnected'
# sound when the connection status becomes green
elif category is sound.SOUND_CONNECTION_GREEN:
soundFilename = state.appdata + 'sounds/green'
if not dt.total_seconds() < self.maxSoundFrequencySec:
# the sound is for an address which exists in the address book
if category is sound.SOUND_KNOWN:
soundFilename = state.appdata + 'sounds/known'
# the sound is for an unknown address
elif category is sound.SOUND_UNKNOWN:
soundFilename = state.appdata + 'sounds/unknown'
# initial connection sound
elif category is sound.SOUND_CONNECTED:
soundFilename = state.appdata + 'sounds/connected'
# disconnected sound
elif category is sound.SOUND_DISCONNECTED:
soundFilename = state.appdata + 'sounds/disconnected'
# sound when the connection status becomes green
elif category is sound.SOUND_CONNECTION_GREEN:
soundFilename = state.appdata + 'sounds/green'
if soundFilename is None:
logger.warning("Probably wrong category number in playSound()")
@ -1336,13 +1425,14 @@ class MyForm(settingsmixin.SMainWindow):
self._player(soundFilename)
# Adapters and converters for QT <-> sqlite
def sqlInit(self):
"""Adapters and converters for QT <-> sqlite"""
register_adapter(QtCore.QByteArray, str)
# Try init the distro specific appindicator,
# for example the Ubuntu MessagingMenu
def indicatorInit(self):
""" Try init the distro specific appindicator, for example the Ubuntu MessagingMenu"""
def _noop_update(*args, **kwargs):
pass
@ -1352,8 +1442,9 @@ class MyForm(settingsmixin.SMainWindow):
logger.warning("No indicator plugin found")
self.indicatorUpdate = _noop_update
# initialise the message notifier
def notifierInit(self):
"""initialise the message notifier"""
def _simple_notify(
title, subtitle, category, label=None, icon=None):
self.tray.showMessage(title, subtitle, 1, 2000)
@ -1383,27 +1474,34 @@ class MyForm(settingsmixin.SMainWindow):
def notifierShow(
self, title, subtitle, category, label=None, icon=None):
"""TBC"""
self.playSound(category, label)
self._notifier(
unicode(title), unicode(subtitle), category, label, icon)
# tree
def treeWidgetKeyPressEvent(self, event):
"""tree"""
return self.handleKeyPress(event, self.getCurrentTreeWidget())
# inbox / sent
def tableWidgetKeyPressEvent(self, event):
"""inbox / sent"""
return self.handleKeyPress(event, self.getCurrentMessagelist())
# messageview
def textEditKeyPressEvent(self, event):
"""messageview"""
return self.handleKeyPress(event, self.getCurrentMessageTextedit())
def handleKeyPress(self, event, focus = None):
def handleKeyPress(self, event, focus=None): # pylint: disable=inconsistent-return-statements
"""TBC"""
messagelist = self.getCurrentMessagelist()
folder = self.getCurrentFolder()
if event.key() == QtCore.Qt.Key_Delete:
if isinstance (focus, MessageView) or isinstance(focus, QtGui.QTableWidget):
if isinstance(focus, (MessageView, QtGui.QTableWidget)):
if folder == "sent":
self.on_action_SentTrash()
else:
@ -1439,60 +1537,116 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.lineEditTo.setFocus()
event.ignore()