From 8a3074f3ff40b4568b2296f821f996b3c440b994 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Tue, 13 Nov 2018 17:03:38 +0200
Subject: [PATCH 01/39] ui-file based Settings dialog
---
src/bitmessageqt/__init__.py | 610 +++---------------
src/bitmessageqt/dialogs.py | 11 +-
src/bitmessageqt/settings.py | 1150 ++++++++++++++++------------------
src/bitmessageqt/settings.ui | 403 +++++++-----
4 files changed, 872 insertions(+), 1302 deletions(-)
diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py
index 2c5f1485..94c00e38 100644
--- a/src/bitmessageqt/__init__.py
+++ b/src/bitmessageqt/__init__.py
@@ -23,7 +23,6 @@ from addresses import decodeAddress, addBMIfNotPresent
import shared
from bitmessageui import Ui_MainWindow
from bmconfigparser import BMConfigParser
-import defaults
import namecoin
from messageview import MessageView
from migrationwizard import Ui_MigrationWizard
@@ -31,15 +30,12 @@ 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 debug
from helper_ackPayload import genAckPayload
from helper_sql import sqlQuery, sqlExecute, sqlExecuteChunked, sqlStoredProcedure
import helper_search
import l10n
-import openclpow
from utils import str_broadcast_subscribers, avatarize
from account import (
getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount,
@@ -47,16 +43,15 @@ from account import (
import dialogs
from network.stats import pendingDownload, pendingUpload
from uisignaler import UISignaler
-import knownnodes
import paths
from proofofwork import getPowType
import queues
import shutdown
import state
from statusbar import BMStatusBar
-from network.asyncore_pollchoose import set_rates
import sound
-
+# This is needed for tray icon
+import bitmessage_icons_rc # noqa:F401 pylint: disable=unused-import
try:
from plugins.plugin import get_plugin, get_plugins
@@ -64,49 +59,6 @@ except ImportError:
get_plugins = False
-def change_translation(newlocale):
- global qmytranslator, qsystranslator
- try:
- if not qmytranslator.isEmpty():
- QtGui.QApplication.removeTranslator(qmytranslator)
- except:
- pass
- try:
- if not qsystranslator.isEmpty():
- QtGui.QApplication.removeTranslator(qsystranslator)
- except:
- pass
-
- qmytranslator = QtCore.QTranslator()
- 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)
- else:
- translationpath = os.path.join (str(QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)), 'qt_' + newlocale)
- qsystranslator.load(translationpath)
- QtGui.QApplication.installTranslator(qsystranslator)
-
- lang = locale.normalize(l10n.getTranslationLanguage())
- langs = [lang.split(".")[0] + "." + l10n.encoding, lang.split(".")[0] + "." + 'UTF-8', lang]
- if 'win32' in sys.platform or 'win64' in sys.platform:
- langs = [l10n.getWindowsLocale(lang)]
- for lang in langs:
- try:
- l10n.setlocale(locale.LC_ALL, lang)
- if 'win32' not in sys.platform and 'win64' not in sys.platform:
- l10n.encoding = locale.nl_langinfo(locale.CODESET)
- else:
- l10n.encoding = locale.getlocale()[1]
- logger.info("Successfully set locale to %s", lang)
- break
- except:
- logger.error("Failed to set locale to %s", lang, exc_info=True)
-
-
# TODO: rewrite
def powQueueSize():
"""Returns the size of queues.workerQueue including current unfinished work"""
@@ -122,9 +74,6 @@ def powQueueSize():
class MyForm(settingsmixin.SMainWindow):
- # the last time that a message arrival sound was played
- lastSoundTime = datetime.now() - timedelta(days=1)
-
# the maximum frequency of message sounds in seconds
maxSoundFrequencySec = 60
@@ -132,6 +81,58 @@ class MyForm(settingsmixin.SMainWindow):
REPLY_TYPE_CHAN = 1
REPLY_TYPE_UPD = 2
+ def change_translation(self, newlocale=None):
+ """Change translation language for the application"""
+ if newlocale is None:
+ newlocale = l10n.getTranslationLanguage()
+ try:
+ if not self.qmytranslator.isEmpty():
+ QtGui.QApplication.removeTranslator(self.qmytranslator)
+ except:
+ pass
+ try:
+ if not self.qsystranslator.isEmpty():
+ QtGui.QApplication.removeTranslator(self.qsystranslator)
+ except:
+ pass
+
+ self.qmytranslator = QtCore.QTranslator()
+ translationpath = os.path.join(
+ paths.codePath(), 'translations', 'bitmessage_' + newlocale)
+ self.qmytranslator.load(translationpath)
+ QtGui.QApplication.installTranslator(self.qmytranslator)
+
+ self.qsystranslator = QtCore.QTranslator()
+ if paths.frozen:
+ translationpath = os.path.join(
+ paths.codePath(), 'translations', 'qt_' + newlocale)
+ else:
+ translationpath = os.path.join(
+ str(QtCore.QLibraryInfo.location(
+ QtCore.QLibraryInfo.TranslationsPath)), 'qt_' + newlocale)
+ self.qsystranslator.load(translationpath)
+ QtGui.QApplication.installTranslator(self.qsystranslator)
+
+ lang = locale.normalize(l10n.getTranslationLanguage())
+ langs = [
+ lang.split(".")[0] + "." + l10n.encoding,
+ lang.split(".")[0] + "." + 'UTF-8',
+ lang
+ ]
+ if 'win32' in sys.platform or 'win64' in sys.platform:
+ langs = [l10n.getWindowsLocale(lang)]
+ for lang in langs:
+ try:
+ l10n.setlocale(locale.LC_ALL, lang)
+ if 'win32' not in sys.platform and 'win64' not in sys.platform:
+ l10n.encoding = locale.nl_langinfo(locale.CODESET)
+ else:
+ l10n.encoding = locale.getlocale()[1]
+ logger.info("Successfully set locale to %s", lang)
+ break
+ except:
+ 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)
@@ -605,6 +606,13 @@ class MyForm(settingsmixin.SMainWindow):
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
+ self.qmytranslator = self.qsystranslator = None
+ self.indicatorUpdate = None
+ self.actionStatus = None
+
+ # the last time that a message arrival sound was played
+ self.lastSoundTime = datetime.now() - timedelta(days=1)
+
# Ask the user if we may delete their old version 1 addresses if they
# have any.
for addressInKeysFile in getSortedAccounts():
@@ -620,26 +628,13 @@ class MyForm(settingsmixin.SMainWindow):
BMConfigParser().remove_section(addressInKeysFile)
BMConfigParser().save()
- # 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.
- if BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'):
- self.settings.setValue("PyBitmessage", sys.argv[0])
- elif 'darwin' in sys.platform:
- # startup for mac
- pass
- elif 'linux' in sys.platform:
- # startup for linux
- pass
+ self.updateStartOnLogon()
+
+ self.change_translation()
# e.g. for editing labels
self.recurDepth = 0
-
+
# switch back to this when replying
self.replyFromTab = None
@@ -828,6 +823,28 @@ class MyForm(settingsmixin.SMainWindow):
finally:
self._contact_selected = None
+ def updateStartOnLogon(self):
+ # 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)
+ # 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:
+ # startup for mac
+ pass
+ elif 'linux' in sys.platform:
+ # startup for linux
+ pass
+
def updateTTL(self, sliderPosition):
TTL = int(sliderPosition ** 3.199 + 3600)
self.updateHumanFriendlyTTLDescription(TTL)
@@ -1622,7 +1639,6 @@ class MyForm(settingsmixin.SMainWindow):
# The window state has just been changed to
# Normal/Maximised/FullScreen
pass
- # QtGui.QWidget.changeEvent(self, event)
def __icon_activated(self, reason):
if reason == QtGui.QSystemTrayIcon.Trigger:
@@ -2434,225 +2450,7 @@ class MyForm(settingsmixin.SMainWindow):
dialogs.AboutDialog(self).exec_()
def click_actionSettings(self):
- self.settingsDialogInstance = settingsDialog(self)
- if self._firstrun:
- self.settingsDialogInstance.ui.tabWidgetSettings.setCurrentIndex(1)
- if self.settingsDialogInstance.exec_():
- if self._firstrun:
- BMConfigParser().remove_option(
- 'bitmessagesettings', 'dontconnect')
- BMConfigParser().set('bitmessagesettings', 'startonlogon', str(
- self.settingsDialogInstance.ui.checkBoxStartOnLogon.isChecked()))
- BMConfigParser().set('bitmessagesettings', 'minimizetotray', str(
- self.settingsDialogInstance.ui.checkBoxMinimizeToTray.isChecked()))
- BMConfigParser().set('bitmessagesettings', 'trayonclose', str(
- self.settingsDialogInstance.ui.checkBoxTrayOnClose.isChecked()))
- BMConfigParser().set('bitmessagesettings', 'hidetrayconnectionnotifications', str(
- self.settingsDialogInstance.ui.checkBoxHideTrayConnectionNotifications.isChecked()))
- BMConfigParser().set('bitmessagesettings', 'showtraynotifications', str(
- self.settingsDialogInstance.ui.checkBoxShowTrayNotifications.isChecked()))
- BMConfigParser().set('bitmessagesettings', 'startintray', str(
- self.settingsDialogInstance.ui.checkBoxStartInTray.isChecked()))
- BMConfigParser().set('bitmessagesettings', 'willinglysendtomobile', str(
- self.settingsDialogInstance.ui.checkBoxWillinglySendToMobile.isChecked()))
- BMConfigParser().set('bitmessagesettings', 'useidenticons', str(
- self.settingsDialogInstance.ui.checkBoxUseIdenticons.isChecked()))
- BMConfigParser().set('bitmessagesettings', 'replybelow', str(
- self.settingsDialogInstance.ui.checkBoxReplyBelow.isChecked()))
-
- lang = str(self.settingsDialogInstance.ui.languageComboBox.itemData(self.settingsDialogInstance.ui.languageComboBox.currentIndex()).toString())
- BMConfigParser().set('bitmessagesettings', 'userlocale', lang)
- change_translation(l10n.getTranslationLanguage())
-
- if int(BMConfigParser().get('bitmessagesettings', 'port')) != int(self.settingsDialogInstance.ui.lineEditTCPPort.text()):
- if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'):
- QtGui.QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate(
- "MainWindow", "You must restart Bitmessage for the port number change to take effect."))
- BMConfigParser().set('bitmessagesettings', 'port', str(
- self.settingsDialogInstance.ui.lineEditTCPPort.text()))
- if self.settingsDialogInstance.ui.checkBoxUPnP.isChecked() != BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'):
- BMConfigParser().set('bitmessagesettings', 'upnp', str(self.settingsDialogInstance.ui.checkBoxUPnP.isChecked()))
- if self.settingsDialogInstance.ui.checkBoxUPnP.isChecked():
- import upnp
- upnpThread = upnp.uPnPThread()
- upnpThread.start()
- #print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText()', self.settingsDialogInstance.ui.comboBoxProxyType.currentText()
- #print 'self.settingsDialogInstance.ui.comboBoxProxyType.currentText())[0:5]', self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5]
- if BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'none' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS':
- if shared.statusIconColor != 'red':
- QtGui.QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate(
- "MainWindow", "Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any)."))
- if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] != 'SOCKS':
- self.statusbar.clearMessage()
- state.resetNetworkProtocolAvailability() # just in case we changed something in the network connectivity
- if self.settingsDialogInstance.ui.comboBoxProxyType.currentText()[0:5] == 'SOCKS':
- BMConfigParser().set('bitmessagesettings', 'socksproxytype', str(
- self.settingsDialogInstance.ui.comboBoxProxyType.currentText()))
- else:
- BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'none')
- BMConfigParser().set('bitmessagesettings', 'socksauthentication', str(
- self.settingsDialogInstance.ui.checkBoxAuthentication.isChecked()))
- BMConfigParser().set('bitmessagesettings', 'sockshostname', str(
- self.settingsDialogInstance.ui.lineEditSocksHostname.text()))
- BMConfigParser().set('bitmessagesettings', 'socksport', str(
- self.settingsDialogInstance.ui.lineEditSocksPort.text()))
- BMConfigParser().set('bitmessagesettings', 'socksusername', str(
- self.settingsDialogInstance.ui.lineEditSocksUsername.text()))
- BMConfigParser().set('bitmessagesettings', 'sockspassword', str(
- self.settingsDialogInstance.ui.lineEditSocksPassword.text()))
- BMConfigParser().set('bitmessagesettings', 'sockslisten', str(
- self.settingsDialogInstance.ui.checkBoxSocksListen.isChecked()))
- try:
- # Rounding to integers just for aesthetics
- BMConfigParser().set('bitmessagesettings', 'maxdownloadrate', str(
- int(float(self.settingsDialogInstance.ui.lineEditMaxDownloadRate.text()))))
- BMConfigParser().set('bitmessagesettings', 'maxuploadrate', str(
- int(float(self.settingsDialogInstance.ui.lineEditMaxUploadRate.text()))))
- except ValueError:
- QtGui.QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate(
- "MainWindow", "Your maximum download and upload rate must be numbers. Ignoring what you typed."))
- else:
- set_rates(BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"),
- BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate"))
-
- BMConfigParser().set('bitmessagesettings', 'maxoutboundconnections', str(
- int(float(self.settingsDialogInstance.ui.lineEditMaxOutboundConnections.text()))))
-
- BMConfigParser().set('bitmessagesettings', 'namecoinrpctype',
- self.settingsDialogInstance.getNamecoinType())
- BMConfigParser().set('bitmessagesettings', 'namecoinrpchost', str(
- self.settingsDialogInstance.ui.lineEditNamecoinHost.text()))
- BMConfigParser().set('bitmessagesettings', 'namecoinrpcport', str(
- self.settingsDialogInstance.ui.lineEditNamecoinPort.text()))
- BMConfigParser().set('bitmessagesettings', 'namecoinrpcuser', str(
- self.settingsDialogInstance.ui.lineEditNamecoinUser.text()))
- BMConfigParser().set('bitmessagesettings', 'namecoinrpcpassword', str(
- self.settingsDialogInstance.ui.lineEditNamecoinPassword.text()))
- self.resetNamecoinConnection()
-
- # Demanded difficulty tab
- if float(self.settingsDialogInstance.ui.lineEditTotalDifficulty.text()) >= 1:
- BMConfigParser().set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(int(float(
- self.settingsDialogInstance.ui.lineEditTotalDifficulty.text()) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
- if float(self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) >= 1:
- BMConfigParser().set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(int(float(
- self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) * defaults.networkDefaultPayloadLengthExtraBytes)))
-
- if self.settingsDialogInstance.ui.comboBoxOpenCL.currentText().toUtf8() != BMConfigParser().safeGet("bitmessagesettings", "opencl"):
- BMConfigParser().set('bitmessagesettings', 'opencl', str(self.settingsDialogInstance.ui.comboBoxOpenCL.currentText()))
- queues.workerQueue.put(('resetPoW', ''))
-
- acceptableDifficultyChanged = False
-
- if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) == 0:
- if BMConfigParser().get('bitmessagesettings','maxacceptablenoncetrialsperbyte') != str(int(float(
- self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)):
- # the user changed the max acceptable total difficulty
- acceptableDifficultyChanged = True
- BMConfigParser().set('bitmessagesettings', 'maxacceptablenoncetrialsperbyte', str(int(float(
- self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
- if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0:
- if BMConfigParser().get('bitmessagesettings','maxacceptablepayloadlengthextrabytes') != str(int(float(
- self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) * defaults.networkDefaultPayloadLengthExtraBytes)):
- # the user changed the max acceptable small message difficulty
- acceptableDifficultyChanged = True
- BMConfigParser().set('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', str(int(float(
- self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) * defaults.networkDefaultPayloadLengthExtraBytes)))
- if acceptableDifficultyChanged:
- # It might now be possible to send msgs which were previously marked as toodifficult.
- # Let us change them to 'msgqueued'. The singleWorker will try to send them and will again
- # mark them as toodifficult if the receiver's required difficulty is still higher than
- # we are willing to do.
- sqlExecute('''UPDATE sent SET status='msgqueued' WHERE status='toodifficult' ''')
- queues.workerQueue.put(('sendmessage', ''))
-
- #start:UI setting to stop trying to send messages after X days/months
- # I'm open to changing this UI to something else if someone has a better idea.
- if ((self.settingsDialogInstance.ui.lineEditDays.text()=='') and (self.settingsDialogInstance.ui.lineEditMonths.text()=='')):#We need to handle this special case. Bitmessage has its default behavior. The input is blank/blank
- BMConfigParser().set('bitmessagesettings', 'stopresendingafterxdays', '')
- BMConfigParser().set('bitmessagesettings', 'stopresendingafterxmonths', '')
- shared.maximumLengthOfTimeToBotherResendingMessages = float('inf')
- try:
- float(self.settingsDialogInstance.ui.lineEditDays.text())
- lineEditDaysIsValidFloat = True
- except:
- lineEditDaysIsValidFloat = False
- try:
- float(self.settingsDialogInstance.ui.lineEditMonths.text())
- lineEditMonthsIsValidFloat = True
- except:
- lineEditMonthsIsValidFloat = False
- if lineEditDaysIsValidFloat and not lineEditMonthsIsValidFloat:
- self.settingsDialogInstance.ui.lineEditMonths.setText("0")
- if lineEditMonthsIsValidFloat and not lineEditDaysIsValidFloat:
- self.settingsDialogInstance.ui.lineEditDays.setText("0")
- if lineEditDaysIsValidFloat or lineEditMonthsIsValidFloat:
- if (float(self.settingsDialogInstance.ui.lineEditDays.text()) >=0 and float(self.settingsDialogInstance.ui.lineEditMonths.text()) >=0):
- shared.maximumLengthOfTimeToBotherResendingMessages = (float(str(self.settingsDialogInstance.ui.lineEditDays.text())) * 24 * 60 * 60) + (float(str(self.settingsDialogInstance.ui.lineEditMonths.text())) * (60 * 60 * 24 *365)/12)
- if shared.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(self, _translate("MainWindow", "Will not resend ever"), _translate(
- "MainWindow", "Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent."))
- BMConfigParser().set('bitmessagesettings', 'stopresendingafterxdays', '0')
- BMConfigParser().set('bitmessagesettings', 'stopresendingafterxmonths', '0')
- shared.maximumLengthOfTimeToBotherResendingMessages = 0
- else:
- BMConfigParser().set('bitmessagesettings', 'stopresendingafterxdays', str(float(
- self.settingsDialogInstance.ui.lineEditDays.text())))
- BMConfigParser().set('bitmessagesettings', 'stopresendingafterxmonths', str(float(
- self.settingsDialogInstance.ui.lineEditMonths.text())))
-
- BMConfigParser().save()
-
- 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)
- if BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'):
- self.settings.setValue("PyBitmessage", sys.argv[0])
- else:
- self.settings.remove("PyBitmessage")
- elif 'darwin' in sys.platform:
- # startup for mac
- pass
- elif 'linux' in sys.platform:
- # startup for linux
- pass
-
- if state.appdata != paths.lookupExeFolder() and self.settingsDialogInstance.ui.checkBoxPortableMode.isChecked(): # If we are NOT using portable mode now but the user selected that we should...
- # Write the keys.dat file to disk in the new location
- sqlStoredProcedure('movemessagstoprog')
- with open(paths.lookupExeFolder() + 'keys.dat', 'wb') as configfile:
- BMConfigParser().write(configfile)
- # Write the knownnodes.dat file to disk in the new location
- knownnodes.saveKnownNodes(paths.lookupExeFolder())
- os.remove(state.appdata + 'keys.dat')
- os.remove(state.appdata + 'knownnodes.dat')
- previousAppdataLocation = state.appdata
- state.appdata = paths.lookupExeFolder()
- debug.resetLogging()
- try:
- os.remove(previousAppdataLocation + 'debug.log')
- os.remove(previousAppdataLocation + 'debug.log.1')
- except:
- pass
-
- if state.appdata == paths.lookupExeFolder() and not self.settingsDialogInstance.ui.checkBoxPortableMode.isChecked(): # If we ARE using portable mode now but the user selected that we shouldn't...
- state.appdata = paths.lookupAppdataFolder()
- if not os.path.exists(state.appdata):
- os.makedirs(state.appdata)
- sqlStoredProcedure('movemessagstoappdata')
- # Write the keys.dat file to disk in the new location
- BMConfigParser().save()
- # Write the knownnodes.dat file to disk in the new location
- knownnodes.saveKnownNodes(state.appdata)
- os.remove(paths.lookupExeFolder() + 'keys.dat')
- os.remove(paths.lookupExeFolder() + 'knownnodes.dat')
- debug.resetLogging()
- try:
- os.remove(paths.lookupExeFolder() + 'debug.log')
- os.remove(paths.lookupExeFolder() + 'debug.log.1')
- except:
- pass
+ dialogs.SettingsDialog(self, firstrun=self._firstrun).exec_()
def on_action_Send(self):
"""Send message to current selected address"""
@@ -4253,237 +4051,6 @@ class MyForm(settingsmixin.SMainWindow):
obj.loadSettings()
-class settingsDialog(QtGui.QDialog):
-
- def __init__(self, parent):
- QtGui.QWidget.__init__(self, parent)
- self.ui = Ui_settingsDialog()
- self.ui.setupUi(self)
- self.parent = parent
- self.ui.checkBoxStartOnLogon.setChecked(
- BMConfigParser().getboolean('bitmessagesettings', 'startonlogon'))
- self.ui.checkBoxMinimizeToTray.setChecked(
- BMConfigParser().getboolean('bitmessagesettings', 'minimizetotray'))
- self.ui.checkBoxTrayOnClose.setChecked(
- BMConfigParser().safeGetBoolean('bitmessagesettings', 'trayonclose'))
- self.ui.checkBoxHideTrayConnectionNotifications.setChecked(
- BMConfigParser().getboolean("bitmessagesettings", "hidetrayconnectionnotifications"))
- self.ui.checkBoxShowTrayNotifications.setChecked(
- BMConfigParser().getboolean('bitmessagesettings', 'showtraynotifications'))
- self.ui.checkBoxStartInTray.setChecked(
- BMConfigParser().getboolean('bitmessagesettings', 'startintray'))
- self.ui.checkBoxWillinglySendToMobile.setChecked(
- BMConfigParser().safeGetBoolean('bitmessagesettings', 'willinglysendtomobile'))
- self.ui.checkBoxUseIdenticons.setChecked(
- BMConfigParser().safeGetBoolean('bitmessagesettings', 'useidenticons'))
- self.ui.checkBoxReplyBelow.setChecked(
- BMConfigParser().safeGetBoolean('bitmessagesettings', 'replybelow'))
-
- if state.appdata == paths.lookupExeFolder():
- self.ui.checkBoxPortableMode.setChecked(True)
- else:
- try:
- import tempfile
- tempfile.NamedTemporaryFile(
- dir=paths.lookupExeFolder(), delete=True
- ).close() # should autodelete
- except:
- self.ui.checkBoxPortableMode.setDisabled(True)
-
- if 'darwin' in sys.platform:
- self.ui.checkBoxStartOnLogon.setDisabled(True)
- self.ui.checkBoxStartOnLogon.setText(_translate(
- "MainWindow", "Start-on-login not yet supported on your OS."))
- self.ui.checkBoxMinimizeToTray.setDisabled(True)
- self.ui.checkBoxMinimizeToTray.setText(_translate(
- "MainWindow", "Minimize-to-tray not yet supported on your OS."))
- self.ui.checkBoxShowTrayNotifications.setDisabled(True)
- self.ui.checkBoxShowTrayNotifications.setText(_translate(
- "MainWindow", "Tray notifications not yet supported on your OS."))
- elif 'linux' in sys.platform:
- self.ui.checkBoxStartOnLogon.setDisabled(True)
- self.ui.checkBoxStartOnLogon.setText(_translate(
- "MainWindow", "Start-on-login not yet supported on your OS."))
- # On the Network settings tab:
- self.ui.lineEditTCPPort.setText(str(
- BMConfigParser().get('bitmessagesettings', 'port')))
- self.ui.checkBoxUPnP.setChecked(
- BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'))
- self.ui.checkBoxAuthentication.setChecked(BMConfigParser().getboolean(
- 'bitmessagesettings', 'socksauthentication'))
- self.ui.checkBoxSocksListen.setChecked(BMConfigParser().getboolean(
- 'bitmessagesettings', 'sockslisten'))
- if str(BMConfigParser().get('bitmessagesettings', 'socksproxytype')) == 'none':
- self.ui.comboBoxProxyType.setCurrentIndex(0)
- self.ui.lineEditSocksHostname.setEnabled(False)
- self.ui.lineEditSocksPort.setEnabled(False)
- self.ui.lineEditSocksUsername.setEnabled(False)
- self.ui.lineEditSocksPassword.setEnabled(False)
- self.ui.checkBoxAuthentication.setEnabled(False)
- self.ui.checkBoxSocksListen.setEnabled(False)
- elif str(BMConfigParser().get('bitmessagesettings', 'socksproxytype')) == 'SOCKS4a':
- self.ui.comboBoxProxyType.setCurrentIndex(1)
- elif str(BMConfigParser().get('bitmessagesettings', 'socksproxytype')) == 'SOCKS5':
- self.ui.comboBoxProxyType.setCurrentIndex(2)
-
- self.ui.lineEditSocksHostname.setText(str(
- BMConfigParser().get('bitmessagesettings', 'sockshostname')))
- self.ui.lineEditSocksPort.setText(str(
- BMConfigParser().get('bitmessagesettings', 'socksport')))
- self.ui.lineEditSocksUsername.setText(str(
- BMConfigParser().get('bitmessagesettings', 'socksusername')))
- self.ui.lineEditSocksPassword.setText(str(
- BMConfigParser().get('bitmessagesettings', 'sockspassword')))
- QtCore.QObject.connect(self.ui.comboBoxProxyType, QtCore.SIGNAL(
- "currentIndexChanged(int)"), self.comboBoxProxyTypeChanged)
- self.ui.lineEditMaxDownloadRate.setText(str(
- BMConfigParser().get('bitmessagesettings', 'maxdownloadrate')))
- self.ui.lineEditMaxUploadRate.setText(str(
- BMConfigParser().get('bitmessagesettings', 'maxuploadrate')))
- self.ui.lineEditMaxOutboundConnections.setText(str(
- BMConfigParser().get('bitmessagesettings', 'maxoutboundconnections')))
-
- # Demanded difficulty tab
- self.ui.lineEditTotalDifficulty.setText(str((float(BMConfigParser().getint(
- 'bitmessagesettings', 'defaultnoncetrialsperbyte')) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
- self.ui.lineEditSmallMessageDifficulty.setText(str((float(BMConfigParser().getint(
- 'bitmessagesettings', 'defaultpayloadlengthextrabytes')) / defaults.networkDefaultPayloadLengthExtraBytes)))
-
- # Max acceptable difficulty tab
- self.ui.lineEditMaxAcceptableTotalDifficulty.setText(str((float(BMConfigParser().getint(
- 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
- self.ui.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(BMConfigParser().getint(
- 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')) / defaults.networkDefaultPayloadLengthExtraBytes)))
-
- # OpenCL
- if openclpow.openclAvailable():
- self.ui.comboBoxOpenCL.setEnabled(True)
- else:
- self.ui.comboBoxOpenCL.setEnabled(False)
- self.ui.comboBoxOpenCL.clear()
- self.ui.comboBoxOpenCL.addItem("None")
- self.ui.comboBoxOpenCL.addItems(openclpow.vendors)
- self.ui.comboBoxOpenCL.setCurrentIndex(0)
- for i in range(self.ui.comboBoxOpenCL.count()):
- if self.ui.comboBoxOpenCL.itemText(i) == BMConfigParser().safeGet('bitmessagesettings', 'opencl'):
- self.ui.comboBoxOpenCL.setCurrentIndex(i)
- break
-
- # Namecoin integration tab
- nmctype = BMConfigParser().get('bitmessagesettings', 'namecoinrpctype')
- self.ui.lineEditNamecoinHost.setText(str(
- BMConfigParser().get('bitmessagesettings', 'namecoinrpchost')))
- self.ui.lineEditNamecoinPort.setText(str(
- BMConfigParser().get('bitmessagesettings', 'namecoinrpcport')))
- self.ui.lineEditNamecoinUser.setText(str(
- BMConfigParser().get('bitmessagesettings', 'namecoinrpcuser')))
- self.ui.lineEditNamecoinPassword.setText(str(
- BMConfigParser().get('bitmessagesettings', 'namecoinrpcpassword')))
-
- if nmctype == "namecoind":
- self.ui.radioButtonNamecoinNamecoind.setChecked(True)
- elif nmctype == "nmcontrol":
- self.ui.radioButtonNamecoinNmcontrol.setChecked(True)
- self.ui.lineEditNamecoinUser.setEnabled(False)
- self.ui.labelNamecoinUser.setEnabled(False)
- self.ui.lineEditNamecoinPassword.setEnabled(False)
- self.ui.labelNamecoinPassword.setEnabled(False)
- else:
- assert False
-
- QtCore.QObject.connect(self.ui.radioButtonNamecoinNamecoind, QtCore.SIGNAL(
- "toggled(bool)"), self.namecoinTypeChanged)
- QtCore.QObject.connect(self.ui.radioButtonNamecoinNmcontrol, QtCore.SIGNAL(
- "toggled(bool)"), self.namecoinTypeChanged)
- QtCore.QObject.connect(self.ui.pushButtonNamecoinTest, QtCore.SIGNAL(
- "clicked()"), self.click_pushButtonNamecoinTest)
-
- #Message Resend tab
- self.ui.lineEditDays.setText(str(
- BMConfigParser().get('bitmessagesettings', 'stopresendingafterxdays')))
- self.ui.lineEditMonths.setText(str(
- BMConfigParser().get('bitmessagesettings', 'stopresendingafterxmonths')))
-
-
- #'System' tab removed for now.
- """try:
- maxCores = BMConfigParser().getint('bitmessagesettings', 'maxcores')
- except:
- maxCores = 99999
- if maxCores <= 1:
- self.ui.comboBoxMaxCores.setCurrentIndex(0)
- elif maxCores == 2:
- self.ui.comboBoxMaxCores.setCurrentIndex(1)
- elif maxCores <= 4:
- self.ui.comboBoxMaxCores.setCurrentIndex(2)
- elif maxCores <= 8:
- self.ui.comboBoxMaxCores.setCurrentIndex(3)
- elif maxCores <= 16:
- self.ui.comboBoxMaxCores.setCurrentIndex(4)
- else:
- self.ui.comboBoxMaxCores.setCurrentIndex(5)"""
-
- QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
-
- def comboBoxProxyTypeChanged(self, comboBoxIndex):
- if comboBoxIndex == 0:
- self.ui.lineEditSocksHostname.setEnabled(False)
- self.ui.lineEditSocksPort.setEnabled(False)
- self.ui.lineEditSocksUsername.setEnabled(False)
- self.ui.lineEditSocksPassword.setEnabled(False)
- self.ui.checkBoxAuthentication.setEnabled(False)
- self.ui.checkBoxSocksListen.setEnabled(False)
- elif comboBoxIndex == 1 or comboBoxIndex == 2:
- self.ui.lineEditSocksHostname.setEnabled(True)
- self.ui.lineEditSocksPort.setEnabled(True)
- self.ui.checkBoxAuthentication.setEnabled(True)
- self.ui.checkBoxSocksListen.setEnabled(True)
- if self.ui.checkBoxAuthentication.isChecked():
- self.ui.lineEditSocksUsername.setEnabled(True)
- self.ui.lineEditSocksPassword.setEnabled(True)
-
- # Check status of namecoin integration radio buttons and translate
- # it to a string as in the options.
- def getNamecoinType(self):
- if self.ui.radioButtonNamecoinNamecoind.isChecked():
- return "namecoind"
- if self.ui.radioButtonNamecoinNmcontrol.isChecked():
- return "nmcontrol"
- assert False
-
- # Namecoin connection type was changed.
- def namecoinTypeChanged(self, checked):
- nmctype = self.getNamecoinType()
- assert nmctype == "namecoind" or nmctype == "nmcontrol"
-
- isNamecoind = (nmctype == "namecoind")
- self.ui.lineEditNamecoinUser.setEnabled(isNamecoind)
- self.ui.labelNamecoinUser.setEnabled(isNamecoind)
- self.ui.lineEditNamecoinPassword.setEnabled(isNamecoind)
- self.ui.labelNamecoinPassword.setEnabled(isNamecoind)
-
- if isNamecoind:
- self.ui.lineEditNamecoinPort.setText(defaults.namecoinDefaultRpcPort)
- else:
- self.ui.lineEditNamecoinPort.setText("9000")
-
- def click_pushButtonNamecoinTest(self):
- """Test the namecoin settings specified in the settings dialog."""
- self.ui.labelNamecoinTestResult.setText(_translate(
- "MainWindow", "Testing..."))
- options = {}
- options["type"] = self.getNamecoinType()
- options["host"] = str(self.ui.lineEditNamecoinHost.text().toUtf8())
- options["port"] = str(self.ui.lineEditNamecoinPort.text().toUtf8())
- options["user"] = str(self.ui.lineEditNamecoinUser.text().toUtf8())
- options["password"] = str(self.ui.lineEditNamecoinPassword.text().toUtf8())
- nc = namecoin.namecoinConnection(options)
- status, text = nc.test()
- self.ui.labelNamecoinTestResult.setText(text)
- if status == 'success':
- self.parent.namecoin = nc
-
-
# In order for the time columns on the Inbox and Sent tabs to be sorted
# correctly (rather than alphabetically), we need to overload the <
# operator and use this class instead of QTableWidgetItem.
@@ -4558,7 +4125,6 @@ def init():
def run():
global myapp
app = init()
- change_translation(l10n.getTranslationLanguage())
app.setStyleSheet("QStatusBar::item { border: 0px solid black }")
myapp = MyForm()
diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py
index 1acdbc3f..b4bcd2fd 100644
--- a/src/bitmessageqt/dialogs.py
+++ b/src/bitmessageqt/dialogs.py
@@ -5,22 +5,25 @@ src/bitmessageqt/dialogs.py
from PyQt4 import QtGui
-from version import softwareVersion
-
import paths
import widgets
from address_dialogs import (
- AddAddressDialog, EmailGatewayDialog, NewAddressDialog, NewSubscriptionDialog, RegenerateAddressesDialog,
+ AddAddressDialog, EmailGatewayDialog, NewAddressDialog,
+ NewSubscriptionDialog, RegenerateAddressesDialog,
SpecialAddressBehaviorDialog
)
from newchandialog import NewChanDialog
from retranslateui import RetranslateMixin
+from settings import SettingsDialog
from tr import _translate
+from version import softwareVersion
+
__all__ = [
"NewChanDialog", "AddAddressDialog", "NewAddressDialog",
"NewSubscriptionDialog", "RegenerateAddressesDialog",
- "SpecialAddressBehaviorDialog", "EmailGatewayDialog"
+ "SpecialAddressBehaviorDialog", "EmailGatewayDialog",
+ "SettingsDialog"
]
diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py
index 3a3db962..fc96b137 100644
--- a/src/bitmessageqt/settings.py
+++ b/src/bitmessageqt/settings.py
@@ -1,630 +1,544 @@
-# -*- coding: utf-8 -*-
-# pylint: disable=too-many-instance-attributes,too-many-locals,too-many-statements,attribute-defined-outside-init
-"""
-src/bitmessageqt/settings.py
-============================
+import os
+import sys
-Form implementation generated from reading ui file 'settings.ui'
+from PyQt4 import QtGui
-Created: Thu Dec 25 23:21:20 2014
- by: PyQt4 UI code generator 4.10.3
-
-WARNING! All changes made in this file will be lost!
-"""
-
-from sys import platform
-
-from PyQt4 import QtCore, QtGui
-
-from . import bitmessage_icons_rc # pylint: disable=unused-import
-from .languagebox import LanguageBox
-
-try:
- _fromUtf8 = QtCore.QString.fromUtf8
-except AttributeError:
- def _fromUtf8(s):
- return s
-
-try:
- _encoding = QtGui.QApplication.UnicodeUTF8
-
- def _translate(context, text, disambig):
- return QtGui.QApplication.translate(context, text, disambig, _encoding)
-except AttributeError:
- def _translate(context, text, disambig):
- return QtGui.QApplication.translate(context, text, disambig)
+import debug
+import defaults
+import knownnodes
+import namecoin
+import openclpow
+import paths
+import queues
+import shared
+import state
+import tempfile
+import widgets
+from bmconfigparser import BMConfigParser
+from helper_sql import sqlExecute, sqlStoredProcedure
+from network.asyncore_pollchoose import set_rates
+from tr import _translate
-class Ui_settingsDialog(object):
- """Encapsulate a UI settings dialog object"""
+class SettingsDialog(QtGui.QDialog):
+ """The "Settings" dialog"""
+ def __init__(self, parent=None, firstrun=False):
+ super(SettingsDialog, self).__init__(parent)
+ widgets.load('settings.ui', self)
- def setupUi(self, settingsDialog):
- """Set up the UI"""
+ self.parent = parent
+ self.firstrun = firstrun
+ self.config = BMConfigParser()
- settingsDialog.setObjectName(_fromUtf8("settingsDialog"))
- settingsDialog.resize(521, 413)
- self.gridLayout = QtGui.QGridLayout(settingsDialog)
- self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
- self.buttonBox = QtGui.QDialogButtonBox(settingsDialog)
- self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
- self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok)
- self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
- self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1)
- self.tabWidgetSettings = QtGui.QTabWidget(settingsDialog)
- self.tabWidgetSettings.setObjectName(_fromUtf8("tabWidgetSettings"))
- self.tabUserInterface = QtGui.QWidget()
- self.tabUserInterface.setEnabled(True)
- self.tabUserInterface.setObjectName(_fromUtf8("tabUserInterface"))
- self.formLayout = QtGui.QFormLayout(self.tabUserInterface)
- self.formLayout.setObjectName(_fromUtf8("formLayout"))
- self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface)
- self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon"))
- self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.checkBoxStartOnLogon)
- self.groupBoxTray = QtGui.QGroupBox(self.tabUserInterface)
- self.groupBoxTray.setObjectName(_fromUtf8("groupBoxTray"))
- self.formLayoutTray = QtGui.QFormLayout(self.groupBoxTray)
- self.formLayoutTray.setObjectName(_fromUtf8("formLayoutTray"))
- self.checkBoxStartInTray = QtGui.QCheckBox(self.groupBoxTray)
- self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray"))
- self.formLayoutTray.setWidget(0, QtGui.QFormLayout.SpanningRole, self.checkBoxStartInTray)
- self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.groupBoxTray)
- self.checkBoxMinimizeToTray.setChecked(True)
- self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray"))
- self.formLayoutTray.setWidget(1, QtGui.QFormLayout.LabelRole, self.checkBoxMinimizeToTray)
- self.checkBoxTrayOnClose = QtGui.QCheckBox(self.groupBoxTray)
- self.checkBoxTrayOnClose.setChecked(True)
- self.checkBoxTrayOnClose.setObjectName(_fromUtf8("checkBoxTrayOnClose"))
- self.formLayoutTray.setWidget(2, QtGui.QFormLayout.LabelRole, self.checkBoxTrayOnClose)
- self.formLayout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.groupBoxTray)
- self.checkBoxHideTrayConnectionNotifications = QtGui.QCheckBox(self.tabUserInterface)
- self.checkBoxHideTrayConnectionNotifications.setChecked(False)
- self.checkBoxHideTrayConnectionNotifications.setObjectName(
- _fromUtf8("checkBoxHideTrayConnectionNotifications"))
- self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.checkBoxHideTrayConnectionNotifications)
- self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface)
- self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications"))
- self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.checkBoxShowTrayNotifications)
- self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface)
- self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode"))
- self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.checkBoxPortableMode)
- self.PortableModeDescription = QtGui.QLabel(self.tabUserInterface)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.PortableModeDescription.sizePolicy().hasHeightForWidth())
- self.PortableModeDescription.setSizePolicy(sizePolicy)
- self.PortableModeDescription.setWordWrap(True)
- self.PortableModeDescription.setObjectName(_fromUtf8("PortableModeDescription"))
- self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.PortableModeDescription)
- self.checkBoxWillinglySendToMobile = QtGui.QCheckBox(self.tabUserInterface)
- self.checkBoxWillinglySendToMobile.setObjectName(_fromUtf8("checkBoxWillinglySendToMobile"))
- self.formLayout.setWidget(6, QtGui.QFormLayout.SpanningRole, self.checkBoxWillinglySendToMobile)
- self.checkBoxUseIdenticons = QtGui.QCheckBox(self.tabUserInterface)
- self.checkBoxUseIdenticons.setObjectName(_fromUtf8("checkBoxUseIdenticons"))
- self.formLayout.setWidget(7, QtGui.QFormLayout.LabelRole, self.checkBoxUseIdenticons)
- self.checkBoxReplyBelow = QtGui.QCheckBox(self.tabUserInterface)
- self.checkBoxReplyBelow.setObjectName(_fromUtf8("checkBoxReplyBelow"))
- self.formLayout.setWidget(8, QtGui.QFormLayout.LabelRole, self.checkBoxReplyBelow)
- self.groupBox = QtGui.QGroupBox(self.tabUserInterface)
- self.groupBox.setObjectName(_fromUtf8("groupBox"))
- self.formLayout_2 = QtGui.QFormLayout(self.groupBox)
- self.formLayout_2.setObjectName(_fromUtf8("formLayout_2"))
- self.languageComboBox = LanguageBox(self.groupBox)
- self.languageComboBox.setMinimumSize(QtCore.QSize(100, 0))
- self.languageComboBox.setObjectName(_fromUtf8("languageComboBox")) # pylint: disable=not-callable
- self.formLayout_2.setWidget(0, QtGui.QFormLayout.LabelRole, self.languageComboBox)
- self.formLayout.setWidget(9, QtGui.QFormLayout.FieldRole, self.groupBox)
- self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8(""))
- self.tabNetworkSettings = QtGui.QWidget()
- self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings"))
- self.gridLayout_4 = QtGui.QGridLayout(self.tabNetworkSettings)
- self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4"))
- self.groupBox1 = QtGui.QGroupBox(self.tabNetworkSettings)
- self.groupBox1.setObjectName(_fromUtf8("groupBox1"))
- self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1)
- self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
- self.label = QtGui.QLabel(self.groupBox1)
- self.label.setObjectName(_fromUtf8("label"))
- self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1, QtCore.Qt.AlignRight)
- self.lineEditTCPPort = QtGui.QLineEdit(self.groupBox1)
- self.lineEditTCPPort.setMaximumSize(QtCore.QSize(70, 16777215))
- self.lineEditTCPPort.setObjectName(_fromUtf8("lineEditTCPPort"))
- self.gridLayout_3.addWidget(self.lineEditTCPPort, 0, 1, 1, 1, QtCore.Qt.AlignLeft)
- self.labelUPnP = QtGui.QLabel(self.groupBox1)
- self.labelUPnP.setObjectName(_fromUtf8("labelUPnP"))
- self.gridLayout_3.addWidget(self.labelUPnP, 0, 2, 1, 1, QtCore.Qt.AlignRight)
- self.checkBoxUPnP = QtGui.QCheckBox(self.groupBox1)
- self.checkBoxUPnP.setObjectName(_fromUtf8("checkBoxUPnP"))
- self.gridLayout_3.addWidget(self.checkBoxUPnP, 0, 3, 1, 1, QtCore.Qt.AlignLeft)
- self.gridLayout_4.addWidget(self.groupBox1, 0, 0, 1, 1)
- self.groupBox_3 = QtGui.QGroupBox(self.tabNetworkSettings)
- self.groupBox_3.setObjectName(_fromUtf8("groupBox_3"))
- self.gridLayout_9 = QtGui.QGridLayout(self.groupBox_3)
- self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9"))
- spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_9.addItem(spacerItem1, 0, 0, 2, 1)
- self.label_24 = QtGui.QLabel(self.groupBox_3)
- self.label_24.setObjectName(_fromUtf8("label_24"))
- self.gridLayout_9.addWidget(self.label_24, 0, 1, 1, 1)
- self.lineEditMaxDownloadRate = QtGui.QLineEdit(self.groupBox_3)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.lineEditMaxDownloadRate.sizePolicy().hasHeightForWidth())
- self.lineEditMaxDownloadRate.setSizePolicy(sizePolicy)
- self.lineEditMaxDownloadRate.setMaximumSize(QtCore.QSize(60, 16777215))
- self.lineEditMaxDownloadRate.setObjectName(_fromUtf8("lineEditMaxDownloadRate"))
- self.gridLayout_9.addWidget(self.lineEditMaxDownloadRate, 0, 2, 1, 1)
- self.label_25 = QtGui.QLabel(self.groupBox_3)
- self.label_25.setObjectName(_fromUtf8("label_25"))
- self.gridLayout_9.addWidget(self.label_25, 1, 1, 1, 1)
- self.lineEditMaxUploadRate = QtGui.QLineEdit(self.groupBox_3)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.lineEditMaxUploadRate.sizePolicy().hasHeightForWidth())
- self.lineEditMaxUploadRate.setSizePolicy(sizePolicy)
- self.lineEditMaxUploadRate.setMaximumSize(QtCore.QSize(60, 16777215))
- self.lineEditMaxUploadRate.setObjectName(_fromUtf8("lineEditMaxUploadRate"))
- self.gridLayout_9.addWidget(self.lineEditMaxUploadRate, 1, 2, 1, 1)
- self.label_26 = QtGui.QLabel(self.groupBox_3)
- self.label_26.setObjectName(_fromUtf8("label_26"))
- self.gridLayout_9.addWidget(self.label_26, 2, 1, 1, 1)
- self.lineEditMaxOutboundConnections = QtGui.QLineEdit(self.groupBox_3)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.lineEditMaxOutboundConnections.sizePolicy().hasHeightForWidth())
- self.lineEditMaxOutboundConnections.setSizePolicy(sizePolicy)
- self.lineEditMaxOutboundConnections.setMaximumSize(QtCore.QSize(60, 16777215))
- self.lineEditMaxOutboundConnections.setObjectName(_fromUtf8("lineEditMaxOutboundConnections"))
self.lineEditMaxOutboundConnections.setValidator(
QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections))
- self.gridLayout_9.addWidget(self.lineEditMaxOutboundConnections, 2, 2, 1, 1)
- self.gridLayout_4.addWidget(self.groupBox_3, 2, 0, 1, 1)
- self.groupBox_2 = QtGui.QGroupBox(self.tabNetworkSettings)
- self.groupBox_2.setObjectName(_fromUtf8("groupBox_2"))
- self.gridLayout_2 = QtGui.QGridLayout(self.groupBox_2)
- self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
- self.label_2 = QtGui.QLabel(self.groupBox_2)
- self.label_2.setObjectName(_fromUtf8("label_2"))
- self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
- self.label_3 = QtGui.QLabel(self.groupBox_2)
- self.label_3.setObjectName(_fromUtf8("label_3"))
- self.gridLayout_2.addWidget(self.label_3, 1, 1, 1, 1)
- self.lineEditSocksHostname = QtGui.QLineEdit(self.groupBox_2)
- self.lineEditSocksHostname.setObjectName(_fromUtf8("lineEditSocksHostname"))
- self.lineEditSocksHostname.setPlaceholderText(_fromUtf8("127.0.0.1"))
- self.gridLayout_2.addWidget(self.lineEditSocksHostname, 1, 2, 1, 2)
- self.label_4 = QtGui.QLabel(self.groupBox_2)
- self.label_4.setObjectName(_fromUtf8("label_4"))
- self.gridLayout_2.addWidget(self.label_4, 1, 4, 1, 1)
- self.lineEditSocksPort = QtGui.QLineEdit(self.groupBox_2)
- self.lineEditSocksPort.setObjectName(_fromUtf8("lineEditSocksPort"))
- if platform in ['darwin', 'win32', 'win64']:
- self.lineEditSocksPort.setPlaceholderText(_fromUtf8("9150"))
+
+ self.adjust_from_config(self.config)
+ if firstrun:
+ # switch to "Network Settings" tab if user selected
+ # "Let me configure special network settings first" on first run
+ self.tabWidgetSettings.setCurrentIndex(
+ self.tabWidgetSettings.indexOf(self.tabNetworkSettings)
+ )
+ QtGui.QWidget.resize(self, QtGui.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
+ self.checkBoxStartOnLogon.setChecked(
+ config.getboolean('bitmessagesettings', 'startonlogon'))
+ self.checkBoxMinimizeToTray.setChecked(
+ config.getboolean('bitmessagesettings', 'minimizetotray'))
+ self.checkBoxTrayOnClose.setChecked(
+ config.safeGetBoolean('bitmessagesettings', 'trayonclose'))
+ self.checkBoxHideTrayConnectionNotifications.setChecked(
+ config.getboolean("bitmessagesettings", "hidetrayconnectionnotifications"))
+ self.checkBoxShowTrayNotifications.setChecked(
+ config.getboolean('bitmessagesettings', 'showtraynotifications'))
+ self.checkBoxStartInTray.setChecked(
+ config.getboolean('bitmessagesettings', 'startintray'))
+ self.checkBoxWillinglySendToMobile.setChecked(
+ config.safeGetBoolean('bitmessagesettings', 'willinglysendtomobile'))
+ self.checkBoxUseIdenticons.setChecked(
+ config.safeGetBoolean('bitmessagesettings', 'useidenticons'))
+ self.checkBoxReplyBelow.setChecked(
+ config.safeGetBoolean('bitmessagesettings', 'replybelow'))
+
+ if state.appdata == paths.lookupExeFolder():
+ self.checkBoxPortableMode.setChecked(True)
else:
- self.lineEditSocksPort.setPlaceholderText(_fromUtf8("9050"))
- self.gridLayout_2.addWidget(self.lineEditSocksPort, 1, 5, 1, 1)
- self.checkBoxAuthentication = QtGui.QCheckBox(self.groupBox_2)
- self.checkBoxAuthentication.setObjectName(_fromUtf8("checkBoxAuthentication"))
- self.gridLayout_2.addWidget(self.checkBoxAuthentication, 2, 1, 1, 1)
- self.label_5 = QtGui.QLabel(self.groupBox_2)
- self.label_5.setObjectName(_fromUtf8("label_5"))
- self.gridLayout_2.addWidget(self.label_5, 2, 2, 1, 1)
- self.lineEditSocksUsername = QtGui.QLineEdit(self.groupBox_2)
- self.lineEditSocksUsername.setEnabled(False)
- self.lineEditSocksUsername.setObjectName(_fromUtf8("lineEditSocksUsername"))
- self.gridLayout_2.addWidget(self.lineEditSocksUsername, 2, 3, 1, 1)
- self.label_6 = QtGui.QLabel(self.groupBox_2)
- self.label_6.setObjectName(_fromUtf8("label_6"))
- self.gridLayout_2.addWidget(self.label_6, 2, 4, 1, 1)
- self.lineEditSocksPassword = QtGui.QLineEdit(self.groupBox_2)
- self.lineEditSocksPassword.setEnabled(False)
- self.lineEditSocksPassword.setInputMethodHints(
- QtCore.Qt.ImhHiddenText | QtCore.Qt.ImhNoAutoUppercase | QtCore.Qt.ImhNoPredictiveText)
- self.lineEditSocksPassword.setEchoMode(QtGui.QLineEdit.Password)
- self.lineEditSocksPassword.setObjectName(_fromUtf8("lineEditSocksPassword"))
- self.gridLayout_2.addWidget(self.lineEditSocksPassword, 2, 5, 1, 1)
- self.checkBoxSocksListen = QtGui.QCheckBox(self.groupBox_2)
- self.checkBoxSocksListen.setObjectName(_fromUtf8("checkBoxSocksListen"))
- self.gridLayout_2.addWidget(self.checkBoxSocksListen, 3, 1, 1, 4)
- self.comboBoxProxyType = QtGui.QComboBox(self.groupBox_2)
- self.comboBoxProxyType.setObjectName(_fromUtf8("comboBoxProxyType")) # pylint: disable=not-callable
- self.comboBoxProxyType.addItem(_fromUtf8(""))
- self.comboBoxProxyType.addItem(_fromUtf8(""))
- self.comboBoxProxyType.addItem(_fromUtf8(""))
- self.gridLayout_2.addWidget(self.comboBoxProxyType, 0, 1, 1, 1)
- self.gridLayout_4.addWidget(self.groupBox_2, 1, 0, 1, 1)
- spacerItem2 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
- self.gridLayout_4.addItem(spacerItem2, 3, 0, 1, 1)
- self.tabWidgetSettings.addTab(self.tabNetworkSettings, _fromUtf8(""))
- self.tabDemandedDifficulty = QtGui.QWidget()
- self.tabDemandedDifficulty.setObjectName(_fromUtf8("tabDemandedDifficulty"))
- self.gridLayout_6 = QtGui.QGridLayout(self.tabDemandedDifficulty)
- self.gridLayout_6.setObjectName(_fromUtf8("gridLayout_6"))
- self.label_9 = QtGui.QLabel(self.tabDemandedDifficulty)
- self.label_9.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.label_9.setObjectName(_fromUtf8("label_9"))
- self.gridLayout_6.addWidget(self.label_9, 1, 1, 1, 1)
- self.label_10 = QtGui.QLabel(self.tabDemandedDifficulty)
- self.label_10.setWordWrap(True)
- self.label_10.setObjectName(_fromUtf8("label_10"))
- self.gridLayout_6.addWidget(self.label_10, 2, 0, 1, 3)
- self.label_11 = QtGui.QLabel(self.tabDemandedDifficulty)
- self.label_11.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.label_11.setObjectName(_fromUtf8("label_11"))
- self.gridLayout_6.addWidget(self.label_11, 3, 1, 1, 1)
- self.label_8 = QtGui.QLabel(self.tabDemandedDifficulty)
- self.label_8.setWordWrap(True)
- self.label_8.setObjectName(_fromUtf8("label_8"))
- self.gridLayout_6.addWidget(self.label_8, 0, 0, 1, 3)
- spacerItem3 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_6.addItem(spacerItem3, 1, 0, 1, 1)
- self.label_12 = QtGui.QLabel(self.tabDemandedDifficulty)
- self.label_12.setWordWrap(True)
- self.label_12.setObjectName(_fromUtf8("label_12"))
- self.gridLayout_6.addWidget(self.label_12, 4, 0, 1, 3)
- self.lineEditSmallMessageDifficulty = QtGui.QLineEdit(self.tabDemandedDifficulty)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.lineEditSmallMessageDifficulty.sizePolicy().hasHeightForWidth())
- self.lineEditSmallMessageDifficulty.setSizePolicy(sizePolicy)
- self.lineEditSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
- self.lineEditSmallMessageDifficulty.setObjectName(_fromUtf8("lineEditSmallMessageDifficulty"))
- self.gridLayout_6.addWidget(self.lineEditSmallMessageDifficulty, 3, 2, 1, 1)
- self.lineEditTotalDifficulty = QtGui.QLineEdit(self.tabDemandedDifficulty)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.lineEditTotalDifficulty.sizePolicy().hasHeightForWidth())
- self.lineEditTotalDifficulty.setSizePolicy(sizePolicy)
- self.lineEditTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
- self.lineEditTotalDifficulty.setObjectName(_fromUtf8("lineEditTotalDifficulty"))
- self.gridLayout_6.addWidget(self.lineEditTotalDifficulty, 1, 2, 1, 1)
- spacerItem4 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_6.addItem(spacerItem4, 3, 0, 1, 1)
- spacerItem5 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
- self.gridLayout_6.addItem(spacerItem5, 5, 0, 1, 1)
- self.tabWidgetSettings.addTab(self.tabDemandedDifficulty, _fromUtf8(""))
- self.tabMaxAcceptableDifficulty = QtGui.QWidget()
- self.tabMaxAcceptableDifficulty.setObjectName(_fromUtf8("tabMaxAcceptableDifficulty"))
- self.gridLayout_7 = QtGui.QGridLayout(self.tabMaxAcceptableDifficulty)
- self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7"))
- self.label_15 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
- self.label_15.setWordWrap(True)
- self.label_15.setObjectName(_fromUtf8("label_15"))
- self.gridLayout_7.addWidget(self.label_15, 0, 0, 1, 3)
- spacerItem6 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_7.addItem(spacerItem6, 1, 0, 1, 1)
- self.label_13 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
- self.label_13.setLayoutDirection(QtCore.Qt.LeftToRight)
- self.label_13.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.label_13.setObjectName(_fromUtf8("label_13"))
- self.gridLayout_7.addWidget(self.label_13, 1, 1, 1, 1)
- self.lineEditMaxAcceptableTotalDifficulty = QtGui.QLineEdit(self.tabMaxAcceptableDifficulty)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.lineEditMaxAcceptableTotalDifficulty.sizePolicy().hasHeightForWidth())
- self.lineEditMaxAcceptableTotalDifficulty.setSizePolicy(sizePolicy)
- self.lineEditMaxAcceptableTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
- self.lineEditMaxAcceptableTotalDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableTotalDifficulty"))
- self.gridLayout_7.addWidget(self.lineEditMaxAcceptableTotalDifficulty, 1, 2, 1, 1)
- spacerItem7 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_7.addItem(spacerItem7, 2, 0, 1, 1)
- self.label_14 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
- self.label_14.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.label_14.setObjectName(_fromUtf8("label_14"))
- self.gridLayout_7.addWidget(self.label_14, 2, 1, 1, 1)
- self.lineEditMaxAcceptableSmallMessageDifficulty = QtGui.QLineEdit(self.tabMaxAcceptableDifficulty)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.lineEditMaxAcceptableSmallMessageDifficulty.sizePolicy().hasHeightForWidth())
- self.lineEditMaxAcceptableSmallMessageDifficulty.setSizePolicy(sizePolicy)
- self.lineEditMaxAcceptableSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
- self.lineEditMaxAcceptableSmallMessageDifficulty.setObjectName(
- _fromUtf8("lineEditMaxAcceptableSmallMessageDifficulty"))
- self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1)
- spacerItem8 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
- self.gridLayout_7.addItem(spacerItem8, 3, 1, 1, 1)
- self.labelOpenCL = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
- self.labelOpenCL.setObjectName(_fromUtf8("labelOpenCL"))
- self.gridLayout_7.addWidget(self.labelOpenCL, 4, 0, 1, 1)
- self.comboBoxOpenCL = QtGui.QComboBox(self.tabMaxAcceptableDifficulty)
- self.comboBoxOpenCL.setObjectName = (_fromUtf8("comboBoxOpenCL"))
- self.gridLayout_7.addWidget(self.comboBoxOpenCL, 4, 1, 1, 1)
- self.tabWidgetSettings.addTab(self.tabMaxAcceptableDifficulty, _fromUtf8(""))
- self.tabNamecoin = QtGui.QWidget()
- self.tabNamecoin.setObjectName(_fromUtf8("tabNamecoin"))
- self.gridLayout_8 = QtGui.QGridLayout(self.tabNamecoin)
- self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8"))
- spacerItem9 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_8.addItem(spacerItem9, 2, 0, 1, 1)
- self.label_16 = QtGui.QLabel(self.tabNamecoin)
- self.label_16.setWordWrap(True)
- self.label_16.setObjectName(_fromUtf8("label_16"))
- self.gridLayout_8.addWidget(self.label_16, 0, 0, 1, 3)
- self.label_17 = QtGui.QLabel(self.tabNamecoin)
- self.label_17.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.label_17.setObjectName(_fromUtf8("label_17"))
- self.gridLayout_8.addWidget(self.label_17, 2, 1, 1, 1)
- self.lineEditNamecoinHost = QtGui.QLineEdit(self.tabNamecoin)
- self.lineEditNamecoinHost.setObjectName(_fromUtf8("lineEditNamecoinHost"))
- self.gridLayout_8.addWidget(self.lineEditNamecoinHost, 2, 2, 1, 1)
- spacerItem10 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_8.addItem(spacerItem10, 3, 0, 1, 1)
- spacerItem11 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_8.addItem(spacerItem11, 4, 0, 1, 1)
- self.label_18 = QtGui.QLabel(self.tabNamecoin)
- self.label_18.setEnabled(True)
- self.label_18.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.label_18.setObjectName(_fromUtf8("label_18"))
- self.gridLayout_8.addWidget(self.label_18, 3, 1, 1, 1)
- self.lineEditNamecoinPort = QtGui.QLineEdit(self.tabNamecoin)
- self.lineEditNamecoinPort.setObjectName(_fromUtf8("lineEditNamecoinPort"))
- self.gridLayout_8.addWidget(self.lineEditNamecoinPort, 3, 2, 1, 1)
- spacerItem12 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
- self.gridLayout_8.addItem(spacerItem12, 8, 1, 1, 1)
- self.labelNamecoinUser = QtGui.QLabel(self.tabNamecoin)
- self.labelNamecoinUser.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.labelNamecoinUser.setObjectName(_fromUtf8("labelNamecoinUser"))
- self.gridLayout_8.addWidget(self.labelNamecoinUser, 4, 1, 1, 1)
- self.lineEditNamecoinUser = QtGui.QLineEdit(self.tabNamecoin)
- self.lineEditNamecoinUser.setObjectName(_fromUtf8("lineEditNamecoinUser"))
- self.gridLayout_8.addWidget(self.lineEditNamecoinUser, 4, 2, 1, 1)
- spacerItem13 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_8.addItem(spacerItem13, 5, 0, 1, 1)
- self.labelNamecoinPassword = QtGui.QLabel(self.tabNamecoin)
- self.labelNamecoinPassword.setAlignment(
- QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.labelNamecoinPassword.setObjectName(_fromUtf8("labelNamecoinPassword"))
- self.gridLayout_8.addWidget(self.labelNamecoinPassword, 5, 1, 1, 1)
- self.lineEditNamecoinPassword = QtGui.QLineEdit(self.tabNamecoin)
- self.lineEditNamecoinPassword.setInputMethodHints(
- QtCore.Qt.ImhHiddenText | QtCore.Qt.ImhNoAutoUppercase | QtCore.Qt.ImhNoPredictiveText)
- self.lineEditNamecoinPassword.setEchoMode(QtGui.QLineEdit.Password)
- self.lineEditNamecoinPassword.setObjectName(_fromUtf8("lineEditNamecoinPassword"))
- self.gridLayout_8.addWidget(self.lineEditNamecoinPassword, 5, 2, 1, 1)
- self.labelNamecoinTestResult = QtGui.QLabel(self.tabNamecoin)
- self.labelNamecoinTestResult.setText(_fromUtf8(""))
- self.labelNamecoinTestResult.setObjectName(_fromUtf8("labelNamecoinTestResult"))
- self.gridLayout_8.addWidget(self.labelNamecoinTestResult, 7, 0, 1, 2)
- self.pushButtonNamecoinTest = QtGui.QPushButton(self.tabNamecoin)
- self.pushButtonNamecoinTest.setObjectName(_fromUtf8("pushButtonNamecoinTest"))
- self.gridLayout_8.addWidget(self.pushButtonNamecoinTest, 7, 2, 1, 1)
- self.horizontalLayout = QtGui.QHBoxLayout()
- self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
- self.label_21 = QtGui.QLabel(self.tabNamecoin)
- self.label_21.setObjectName(_fromUtf8("label_21"))
- self.horizontalLayout.addWidget(self.label_21)
- self.radioButtonNamecoinNamecoind = QtGui.QRadioButton(self.tabNamecoin)
- self.radioButtonNamecoinNamecoind.setObjectName(_fromUtf8("radioButtonNamecoinNamecoind"))
- self.horizontalLayout.addWidget(self.radioButtonNamecoinNamecoind)
- self.radioButtonNamecoinNmcontrol = QtGui.QRadioButton(self.tabNamecoin)
- self.radioButtonNamecoinNmcontrol.setObjectName(_fromUtf8("radioButtonNamecoinNmcontrol"))
- self.horizontalLayout.addWidget(self.radioButtonNamecoinNmcontrol)
- self.gridLayout_8.addLayout(self.horizontalLayout, 1, 0, 1, 3)
- self.tabWidgetSettings.addTab(self.tabNamecoin, _fromUtf8(""))
- self.tabResendsExpire = QtGui.QWidget()
- self.tabResendsExpire.setObjectName(_fromUtf8("tabResendsExpire"))
- self.gridLayout_5 = QtGui.QGridLayout(self.tabResendsExpire)
- self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5"))
- self.label_7 = QtGui.QLabel(self.tabResendsExpire)
- self.label_7.setWordWrap(True)
- self.label_7.setObjectName(_fromUtf8("label_7"))
- self.gridLayout_5.addWidget(self.label_7, 0, 0, 1, 3)
- spacerItem14 = QtGui.QSpacerItem(212, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.gridLayout_5.addItem(spacerItem14, 1, 0, 1, 1)
- self.widget = QtGui.QWidget(self.tabResendsExpire)
- self.widget.setMinimumSize(QtCore.QSize(231, 75))
- self.widget.setObjectName(_fromUtf8("widget"))
- self.label_19 = QtGui.QLabel(self.widget)
- self.label_19.setGeometry(QtCore.QRect(10, 20, 101, 20))
- self.label_19.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.label_19.setObjectName(_fromUtf8("label_19"))
- self.label_20 = QtGui.QLabel(self.widget)
- self.label_20.setGeometry(QtCore.QRect(30, 40, 80, 16))
- self.label_20.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
- self.label_20.setObjectName(_fromUtf8("label_20"))
- self.lineEditDays = QtGui.QLineEdit(self.widget)
- self.lineEditDays.setGeometry(QtCore.QRect(113, 20, 51, 20))
- self.lineEditDays.setObjectName(_fromUtf8("lineEditDays"))
- self.lineEditMonths = QtGui.QLineEdit(self.widget)
- self.lineEditMonths.setGeometry(QtCore.QRect(113, 40, 51, 20))
- self.lineEditMonths.setObjectName(_fromUtf8("lineEditMonths"))
- self.label_22 = QtGui.QLabel(self.widget)
- self.label_22.setGeometry(QtCore.QRect(169, 23, 61, 16))
- self.label_22.setObjectName(_fromUtf8("label_22"))
- self.label_23 = QtGui.QLabel(self.widget)
- self.label_23.setGeometry(QtCore.QRect(170, 41, 71, 16))
- self.label_23.setObjectName(_fromUtf8("label_23"))
- self.gridLayout_5.addWidget(self.widget, 1, 2, 1, 1)
- spacerItem15 = QtGui.QSpacerItem(20, 129, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
- self.gridLayout_5.addItem(spacerItem15, 2, 1, 1, 1)
- self.tabWidgetSettings.addTab(self.tabResendsExpire, _fromUtf8(""))
- self.gridLayout.addWidget(self.tabWidgetSettings, 0, 0, 1, 1)
+ try:
+ tempfile.NamedTemporaryFile(
+ dir=paths.lookupExeFolder(), delete=True
+ ).close() # should autodelete
+ except:
+ self.checkBoxPortableMode.setDisabled(True)
- self.retranslateUi(settingsDialog)
- self.tabWidgetSettings.setCurrentIndex(0)
- QtCore.QObject.connect( # pylint: disable=no-member
- self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), settingsDialog.accept)
- QtCore.QObject.connect( # pylint: disable=no-member
- self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), settingsDialog.reject)
- QtCore.QObject.connect( # pylint: disable=no-member
- self.checkBoxAuthentication,
- QtCore.SIGNAL(
- _fromUtf8("toggled(bool)")),
- self.lineEditSocksUsername.setEnabled)
- QtCore.QObject.connect( # pylint: disable=no-member
- self.checkBoxAuthentication,
- QtCore.SIGNAL(
- _fromUtf8("toggled(bool)")),
- self.lineEditSocksPassword.setEnabled)
- QtCore.QMetaObject.connectSlotsByName(settingsDialog)
- settingsDialog.setTabOrder(self.tabWidgetSettings, self.checkBoxStartOnLogon)
- settingsDialog.setTabOrder(self.checkBoxStartOnLogon, self.checkBoxStartInTray)
- settingsDialog.setTabOrder(self.checkBoxStartInTray, self.checkBoxMinimizeToTray)
- settingsDialog.setTabOrder(self.checkBoxMinimizeToTray, self.lineEditTCPPort)
- settingsDialog.setTabOrder(self.lineEditTCPPort, self.comboBoxProxyType)
- settingsDialog.setTabOrder(self.comboBoxProxyType, self.lineEditSocksHostname)
- settingsDialog.setTabOrder(self.lineEditSocksHostname, self.lineEditSocksPort)
- settingsDialog.setTabOrder(self.lineEditSocksPort, self.checkBoxAuthentication)
- settingsDialog.setTabOrder(self.checkBoxAuthentication, self.lineEditSocksUsername)
- settingsDialog.setTabOrder(self.lineEditSocksUsername, self.lineEditSocksPassword)
- settingsDialog.setTabOrder(self.lineEditSocksPassword, self.checkBoxSocksListen)
- settingsDialog.setTabOrder(self.checkBoxSocksListen, self.buttonBox)
+ if 'darwin' in sys.platform:
+ self.checkBoxStartOnLogon.setDisabled(True)
+ self.checkBoxStartOnLogon.setText(_translate(
+ "MainWindow", "Start-on-login not yet supported on your OS."))
+ self.checkBoxMinimizeToTray.setDisabled(True)
+ self.checkBoxMinimizeToTray.setText(_translate(
+ "MainWindow", "Minimize-to-tray not yet supported on your OS."))
+ self.checkBoxShowTrayNotifications.setDisabled(True)
+ self.checkBoxShowTrayNotifications.setText(_translate(
+ "MainWindow", "Tray notifications not yet supported on your OS."))
+ elif 'linux' in sys.platform:
+ self.checkBoxStartOnLogon.setDisabled(True)
+ self.checkBoxStartOnLogon.setText(_translate(
+ "MainWindow", "Start-on-login not yet supported on your OS."))
+ # On the Network settings tab:
+ self.lineEditTCPPort.setText(str(
+ config.get('bitmessagesettings', 'port')))
+ self.checkBoxUPnP.setChecked(
+ config.safeGetBoolean('bitmessagesettings', 'upnp'))
+ self.checkBoxAuthentication.setChecked(
+ config.getboolean('bitmessagesettings', 'socksauthentication'))
+ self.checkBoxSocksListen.setChecked(
+ config.getboolean('bitmessagesettings', 'sockslisten'))
- def retranslateUi(self, settingsDialog):
- """Re-translate the UI into the supported languages"""
+ proxy_type = config.get('bitmessagesettings', 'socksproxytype')
+ if proxy_type == 'none':
+ self.comboBoxProxyType.setCurrentIndex(0)
+ self.lineEditSocksHostname.setEnabled(False)
+ self.lineEditSocksPort.setEnabled(False)
+ self.lineEditSocksUsername.setEnabled(False)
+ self.lineEditSocksPassword.setEnabled(False)
+ self.checkBoxAuthentication.setEnabled(False)
+ self.checkBoxSocksListen.setEnabled(False)
+ elif proxy_type == 'SOCKS4a':
+ self.comboBoxProxyType.setCurrentIndex(1)
+ elif proxy_type == 'SOCKS5':
+ self.comboBoxProxyType.setCurrentIndex(2)
- settingsDialog.setWindowTitle(_translate("settingsDialog", "Settings", None))
- self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None))
- self.groupBoxTray.setTitle(_translate("settingsDialog", "Tray", None))
- self.checkBoxStartInTray.setText(
- _translate(
- "settingsDialog",
- "Start Bitmessage in the tray (don\'t show main window)",
- None))
- self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None))
- self.checkBoxTrayOnClose.setText(_translate("settingsDialog", "Close to tray", None))
- self.checkBoxHideTrayConnectionNotifications.setText(
- _translate("settingsDialog", "Hide connection notifications", None))
- self.checkBoxShowTrayNotifications.setText(
- _translate(
- "settingsDialog",
- "Show notification when message received",
- None))
- self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None))
- self.PortableModeDescription.setText(
- _translate(
- "settingsDialog",
- "In Portable Mode, messages and config files are stored in the same directory as the"
- " program rather than the normal application-data folder. This makes it convenient to"
- " run Bitmessage from a USB thumb drive.",
- None))
- self.checkBoxWillinglySendToMobile.setText(
- _translate(
- "settingsDialog",
- "Willingly include unencrypted destination address when sending to a mobile device",
- None))
- self.checkBoxUseIdenticons.setText(_translate("settingsDialog", "Use Identicons", None))
- self.checkBoxReplyBelow.setText(_translate("settingsDialog", "Reply below Quote", None))
- self.groupBox.setTitle(_translate("settingsDialog", "Interface Language", None))
- self.languageComboBox.setItemText(0, _translate("settingsDialog", "System Settings", "system"))
- self.tabWidgetSettings.setTabText(
- self.tabWidgetSettings.indexOf(
- self.tabUserInterface),
- _translate(
- "settingsDialog", "User Interface", None))
- self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None))
- self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None))
- self.labelUPnP.setText(_translate("settingsDialog", "UPnP:", None))
- self.groupBox_3.setTitle(_translate("settingsDialog", "Bandwidth limit", None))
- self.label_24.setText(_translate("settingsDialog", "Maximum download rate (kB/s): [0: unlimited]", None))
- self.label_25.setText(_translate("settingsDialog", "Maximum upload rate (kB/s): [0: unlimited]", None))
- self.label_26.setText(_translate("settingsDialog", "Maximum outbound connections: [0: none]", None))
- self.groupBox_2.setTitle(_translate("settingsDialog", "Proxy server / Tor", None))
- self.label_2.setText(_translate("settingsDialog", "Type:", None))
- self.label_3.setText(_translate("settingsDialog", "Server hostname:", None))
- self.label_4.setText(_translate("settingsDialog", "Port:", None))
- self.checkBoxAuthentication.setText(_translate("settingsDialog", "Authentication", None))
- self.label_5.setText(_translate("settingsDialog", "Username:", None))
- self.label_6.setText(_translate("settingsDialog", "Pass:", None))
- self.checkBoxSocksListen.setText(
- _translate(
- "settingsDialog",
- "Listen for incoming connections when using proxy",
- None))
- self.comboBoxProxyType.setItemText(0, _translate("settingsDialog", "none", None))
- self.comboBoxProxyType.setItemText(1, _translate("settingsDialog", "SOCKS4a", None))
- self.comboBoxProxyType.setItemText(2, _translate("settingsDialog", "SOCKS5", None))
- self.tabWidgetSettings.setTabText(
- self.tabWidgetSettings.indexOf(
- self.tabNetworkSettings),
- _translate(
- "settingsDialog", "Network Settings", None))
- self.label_9.setText(_translate("settingsDialog", "Total difficulty:", None))
- self.label_10.setText(
- _translate(
- "settingsDialog",
- "The \'Total difficulty\' affects the absolute amount of work the sender must complete."
- " Doubling this value doubles the amount of work.",
- None))
- self.label_11.setText(_translate("settingsDialog", "Small message difficulty:", None))
- self.label_8.setText(_translate(
- "settingsDialog",
- "When someone sends you a message, their computer must first complete some work. The difficulty of this"
- " work, by default, is 1. You may raise this default for new addresses you create by changing the values"
- " here. Any new addresses you create will require senders to meet the higher difficulty. There is one"
- " exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically"
- " notify them when you next send a message that they need only complete the minimum amount of"
- " work: difficulty 1. ",
- None))
- self.label_12.setText(
- _translate(
- "settingsDialog",
- "The \'Small message difficulty\' mostly only affects the difficulty of sending small messages."
- " Doubling this value makes it almost twice as difficult to send a small message but doesn\'t really"
- " affect large messages.",
- None))
- self.tabWidgetSettings.setTabText(
- self.tabWidgetSettings.indexOf(
- self.tabDemandedDifficulty),
- _translate(
- "settingsDialog", "Demanded difficulty", None))
- self.label_15.setText(
- _translate(
- "settingsDialog",
- "Here you may set the maximum amount of work you are willing to do to send a message to another"
- " person. Setting these values to 0 means that any value is acceptable.",
- None))
- self.label_13.setText(_translate("settingsDialog", "Maximum acceptable total difficulty:", None))
- self.label_14.setText(_translate("settingsDialog", "Maximum acceptable small message difficulty:", None))
- self.tabWidgetSettings.setTabText(
- self.tabWidgetSettings.indexOf(
- self.tabMaxAcceptableDifficulty),
- _translate(
- "settingsDialog", "Max acceptable difficulty", None))
- self.labelOpenCL.setText(_translate("settingsDialog", "Hardware GPU acceleration (OpenCL):", None))
- self.label_16.setText(_translate(
- "settingsDialog",
- "
Bitmessage can utilize a different Bitcoin-based program called Namecoin to make"
- " addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage"
- " address, you can simply tell him to send a message to test."
- "
(Getting your own Bitmessage address into Namecoin is still rather difficult).
"
- "Bitmessage can use either namecoind directly or a running nmcontrol instance.
",
- None))
- self.label_17.setText(_translate("settingsDialog", "Host:", None))
- self.label_18.setText(_translate("settingsDialog", "Port:", None))
- self.labelNamecoinUser.setText(_translate("settingsDialog", "Username:", None))
- self.labelNamecoinPassword.setText(_translate("settingsDialog", "Password:", None))
- self.pushButtonNamecoinTest.setText(_translate("settingsDialog", "Test", None))
- self.label_21.setText(_translate("settingsDialog", "Connect to:", None))
- self.radioButtonNamecoinNamecoind.setText(_translate("settingsDialog", "Namecoind", None))
- self.radioButtonNamecoinNmcontrol.setText(_translate("settingsDialog", "NMControl", None))
- self.tabWidgetSettings.setTabText(
- self.tabWidgetSettings.indexOf(
- self.tabNamecoin),
- _translate(
- "settingsDialog", "Namecoin integration", None))
- self.label_7.setText(_translate(
- "settingsDialog",
- "By default, if you send a message to someone and he is offline for more than two"
- " days, Bitmessage will send the message again after an additional two days. This will be continued with"
- " exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver"
- " acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain"
- " number of days or months.
Leave these input fields blank for the default behavior."
- "
",
- None))
- self.label_19.setText(_translate("settingsDialog", "Give up after", None))
- self.label_20.setText(_translate("settingsDialog", "and", None))
- self.label_22.setText(_translate("settingsDialog", "days", None))
- self.label_23.setText(_translate("settingsDialog", "months.", None))
- self.tabWidgetSettings.setTabText(
- self.tabWidgetSettings.indexOf(
- self.tabResendsExpire),
- _translate(
- "settingsDialog", "Resends Expire", None))
+ self.lineEditSocksHostname.setText(
+ config.get('bitmessagesettings', 'sockshostname'))
+ self.lineEditSocksPort.setText(str(
+ config.get('bitmessagesettings', 'socksport')))
+ self.lineEditSocksUsername.setText(
+ config.get('bitmessagesettings', 'socksusername'))
+ self.lineEditSocksPassword.setText(
+ config.get('bitmessagesettings', 'sockspassword'))
+
+ self.lineEditMaxDownloadRate.setText(str(
+ config.get('bitmessagesettings', 'maxdownloadrate')))
+ self.lineEditMaxUploadRate.setText(str(
+ config.get('bitmessagesettings', 'maxuploadrate')))
+ self.lineEditMaxOutboundConnections.setText(str(
+ config.get('bitmessagesettings', 'maxoutboundconnections')))
+
+ # Demanded difficulty tab
+ self.lineEditTotalDifficulty.setText(str((float(
+ config.getint(
+ 'bitmessagesettings', 'defaultnoncetrialsperbyte')
+ ) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
+ self.lineEditSmallMessageDifficulty.setText(str((float(
+ config.getint(
+ 'bitmessagesettings', 'defaultpayloadlengthextrabytes')
+ ) / defaults.networkDefaultPayloadLengthExtraBytes)))
+
+ # Max acceptable difficulty tab
+ self.lineEditMaxAcceptableTotalDifficulty.setText(str((float(
+ config.getint(
+ 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
+ ) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
+ self.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(
+ config.getint(
+ 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')
+ ) / defaults.networkDefaultPayloadLengthExtraBytes)))
+
+ # OpenCL
+ self.comboBoxOpenCL.setEnabled(openclpow.openclAvailable())
+ self.comboBoxOpenCL.clear()
+ self.comboBoxOpenCL.addItem("None")
+ self.comboBoxOpenCL.addItems(openclpow.vendors)
+ self.comboBoxOpenCL.setCurrentIndex(0)
+ for i in range(self.comboBoxOpenCL.count()):
+ if self.comboBoxOpenCL.itemText(i) == config.safeGet(
+ 'bitmessagesettings', 'opencl'):
+ self.comboBoxOpenCL.setCurrentIndex(i)
+ break
+
+ # Namecoin integration tab
+ nmctype = config.get('bitmessagesettings', 'namecoinrpctype')
+ self.lineEditNamecoinHost.setText(
+ config.get('bitmessagesettings', 'namecoinrpchost'))
+ self.lineEditNamecoinPort.setText(str(
+ config.get('bitmessagesettings', 'namecoinrpcport')))
+ self.lineEditNamecoinUser.setText(
+ config.get('bitmessagesettings', 'namecoinrpcuser'))
+ self.lineEditNamecoinPassword.setText(
+ config.get('bitmessagesettings', 'namecoinrpcpassword'))
+
+ if nmctype == "namecoind":
+ self.radioButtonNamecoinNamecoind.setChecked(True)
+ elif nmctype == "nmcontrol":
+ self.radioButtonNamecoinNmcontrol.setChecked(True)
+ self.lineEditNamecoinUser.setEnabled(False)
+ self.labelNamecoinUser.setEnabled(False)
+ self.lineEditNamecoinPassword.setEnabled(False)
+ self.labelNamecoinPassword.setEnabled(False)
+ else:
+ assert False
+
+ # Message Resend tab
+ self.lineEditDays.setText(str(
+ config.get('bitmessagesettings', 'stopresendingafterxdays')))
+ self.lineEditMonths.setText(str(
+ config.get('bitmessagesettings', 'stopresendingafterxmonths')))
+
+ def comboBoxProxyTypeChanged(self, comboBoxIndex):
+ """A callback for currentIndexChanged event of comboBoxProxyType"""
+ if comboBoxIndex == 0:
+ self.lineEditSocksHostname.setEnabled(False)
+ self.lineEditSocksPort.setEnabled(False)
+ self.lineEditSocksUsername.setEnabled(False)
+ self.lineEditSocksPassword.setEnabled(False)
+ self.checkBoxAuthentication.setEnabled(False)
+ self.checkBoxSocksListen.setEnabled(False)
+ elif comboBoxIndex in (1, 2):
+ self.lineEditSocksHostname.setEnabled(True)
+ self.lineEditSocksPort.setEnabled(True)
+ self.checkBoxAuthentication.setEnabled(True)
+ self.checkBoxSocksListen.setEnabled(True)
+ if self.checkBoxAuthentication.isChecked():
+ self.lineEditSocksUsername.setEnabled(True)
+ self.lineEditSocksPassword.setEnabled(True)
+
+ def getNamecoinType(self):
+ """
+ Check status of namecoin integration radio buttons
+ and translate it to a string as in the options.
+ """
+ if self.radioButtonNamecoinNamecoind.isChecked():
+ return "namecoind"
+ if self.radioButtonNamecoinNmcontrol.isChecked():
+ return "nmcontrol"
+ assert False
+
+ # Namecoin connection type was changed.
+ def namecoinTypeChanged(self, checked): # pylint: disable=unused-argument
+ """A callback for toggled event of radioButtonNamecoinNamecoind"""
+ nmctype = self.getNamecoinType()
+ assert nmctype == "namecoind" or nmctype == "nmcontrol"
+
+ isNamecoind = (nmctype == "namecoind")
+ self.lineEditNamecoinUser.setEnabled(isNamecoind)
+ self.labelNamecoinUser.setEnabled(isNamecoind)
+ self.lineEditNamecoinPassword.setEnabled(isNamecoind)
+ self.labelNamecoinPassword.setEnabled(isNamecoind)
+
+ if isNamecoind:
+ self.lineEditNamecoinPort.setText(defaults.namecoinDefaultRpcPort)
+ else:
+ self.lineEditNamecoinPort.setText("9000")
+
+ def click_pushButtonNamecoinTest(self):
+ """Test the namecoin settings specified in the settings dialog."""
+ self.labelNamecoinTestResult.setText(
+ _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())
+ })
+ status, text = nc.test()
+ self.labelNamecoinTestResult.setText(text)
+ if status == 'success':
+ self.parent.namecoin = nc
+
+ def accept(self):
+ """A callback for accepted event of buttonBox (OK button pressed)"""
+ # pylint: disable=too-many-branches,too-many-statements
+ super(SettingsDialog, self).accept()
+ if self.firstrun:
+ self.config.remove_option('bitmessagesettings', 'dontconnect')
+ self.config.set('bitmessagesettings', 'startonlogon', str(
+ self.checkBoxStartOnLogon.isChecked()))
+ self.config.set('bitmessagesettings', 'minimizetotray', str(
+ self.checkBoxMinimizeToTray.isChecked()))
+ self.config.set('bitmessagesettings', 'trayonclose', str(
+ self.checkBoxTrayOnClose.isChecked()))
+ self.config.set(
+ 'bitmessagesettings', 'hidetrayconnectionnotifications',
+ str(self.checkBoxHideTrayConnectionNotifications.isChecked()))
+ self.config.set('bitmessagesettings', 'showtraynotifications', str(
+ self.checkBoxShowTrayNotifications.isChecked()))
+ self.config.set('bitmessagesettings', 'startintray', str(
+ self.checkBoxStartInTray.isChecked()))
+ self.config.set('bitmessagesettings', 'willinglysendtomobile', str(
+ self.checkBoxWillinglySendToMobile.isChecked()))
+ self.config.set('bitmessagesettings', 'useidenticons', str(
+ self.checkBoxUseIdenticons.isChecked()))
+ self.config.set('bitmessagesettings', 'replybelow', str(
+ self.checkBoxReplyBelow.isChecked()))
+
+ lang = str(self.languageComboBox.itemData(
+ self.languageComboBox.currentIndex()).toString())
+ self.config.set('bitmessagesettings', 'userlocale', lang)
+ self.parent.change_translation()
+
+ if int(self.config.get('bitmessagesettings', 'port')) != int(
+ self.lineEditTCPPort.text()):
+ if not self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
+ QtGui.QMessageBox.about(
+ self, _translate("MainWindow", "Restart"),
+ _translate(
+ "MainWindow",
+ "You must restart Bitmessage for the port number"
+ " change to take effect.")
+ )
+ self.config.set(
+ 'bitmessagesettings', 'port', str(self.lineEditTCPPort.text()))
+ if self.checkBoxUPnP.isChecked() != self.config.safeGetBoolean(
+ 'bitmessagesettings', 'upnp'):
+ self.config.set(
+ 'bitmessagesettings', 'upnp',
+ str(self.checkBoxUPnP.isChecked()))
+ if self.checkBoxUPnP.isChecked():
+ import upnp
+ upnpThread = upnp.uPnPThread()
+ upnpThread.start()
+
+ if (
+ self.config.get('bitmessagesettings', 'socksproxytype') ==
+ 'none' and
+ self.comboBoxProxyType.currentText()[0:5] == 'SOCKS'
+ ):
+ if shared.statusIconColor != 'red':
+ QtGui.QMessageBox.about(
+ self, _translate("MainWindow", "Restart"),
+ _translate(
+ "MainWindow",
+ "Bitmessage will use your proxy from now on but"
+ " you may want to manually restart Bitmessage now"
+ " to close existing connections (if any).")
+ )
+ if (
+ self.config.get('bitmessagesettings', 'socksproxytype')[0:5] ==
+ 'SOCKS' and self.comboBoxProxyType.currentText()[0:5] != 'SOCKS'
+ ):
+ self.parent.statusbar.clearMessage()
+ # just in case we changed something in the network connectivity
+ state.resetNetworkProtocolAvailability()
+ if self.comboBoxProxyType.currentText()[0:5] == 'SOCKS':
+ self.config.set('bitmessagesettings', 'socksproxytype', str(
+ self.comboBoxProxyType.currentText()))
+ else:
+ self.config.set('bitmessagesettings', 'socksproxytype', 'none')
+ self.config.set('bitmessagesettings', 'socksauthentication', str(
+ self.checkBoxAuthentication.isChecked()))
+ self.config.set('bitmessagesettings', 'sockshostname', str(
+ self.lineEditSocksHostname.text()))
+ self.config.set('bitmessagesettings', 'socksport', str(
+ self.lineEditSocksPort.text()))
+ self.config.set('bitmessagesettings', 'socksusername', str(
+ self.lineEditSocksUsername.text()))
+ self.config.set('bitmessagesettings', 'sockspassword', str(
+ self.lineEditSocksPassword.text()))
+ self.config.set('bitmessagesettings', 'sockslisten', str(
+ self.checkBoxSocksListen.isChecked()))
+ try:
+ # Rounding to integers just for aesthetics
+ self.config.set('bitmessagesettings', 'maxdownloadrate', str(
+ int(float(self.lineEditMaxDownloadRate.text()))))
+ self.config.set('bitmessagesettings', 'maxuploadrate', str(
+ int(float(self.lineEditMaxUploadRate.text()))))
+ except ValueError:
+ QtGui.QMessageBox.about(
+ self, _translate("MainWindow", "Number needed"),
+ _translate(
+ "MainWindow",
+ "Your maximum download and upload rate must be numbers."
+ " Ignoring what you typed.")
+ )
+ else:
+ set_rates(
+ self.config.safeGetInt('bitmessagesettings', 'maxdownloadrate'),
+ self.config.safeGetInt('bitmessagesettings', 'maxuploadrate'))
+
+ self.config.set('bitmessagesettings', 'maxoutboundconnections', str(
+ int(float(self.lineEditMaxOutboundConnections.text()))))
+
+ self.config.set(
+ 'bitmessagesettings', 'namecoinrpctype', self.getNamecoinType())
+ self.config.set('bitmessagesettings', 'namecoinrpchost', str(
+ self.lineEditNamecoinHost.text()))
+ self.config.set('bitmessagesettings', 'namecoinrpcport', str(
+ self.lineEditNamecoinPort.text()))
+ self.config.set('bitmessagesettings', 'namecoinrpcuser', str(
+ self.lineEditNamecoinUser.text()))
+ self.config.set('bitmessagesettings', 'namecoinrpcpassword', str(
+ self.lineEditNamecoinPassword.text()))
+ self.parent.resetNamecoinConnection()
+
+ # Demanded difficulty tab
+ if float(self.lineEditTotalDifficulty.text()) >= 1:
+ self.config.set(
+ 'bitmessagesettings', 'defaultnoncetrialsperbyte',
+ str(int(
+ float(self.lineEditTotalDifficulty.text()) *
+ defaults.networkDefaultProofOfWorkNonceTrialsPerByte)))
+ if float(self.lineEditSmallMessageDifficulty.text()) >= 1:
+ self.config.set(
+ 'bitmessagesettings', 'defaultpayloadlengthextrabytes',
+ str(int(
+ float(self.lineEditSmallMessageDifficulty.text()) *
+ defaults.networkDefaultPayloadLengthExtraBytes)))
+
+ if self.comboBoxOpenCL.currentText().toUtf8() != self.config.safeGet(
+ 'bitmessagesettings', 'opencl'):
+ self.config.set(
+ 'bitmessagesettings', 'opencl',
+ str(self.comboBoxOpenCL.currentText()))
+ queues.workerQueue.put(('resetPoW', ''))
+
+ acceptableDifficultyChanged = False
+
+ if (
+ float(self.lineEditMaxAcceptableTotalDifficulty.text()) >= 1 or
+ float(self.lineEditMaxAcceptableTotalDifficulty.text()) == 0
+ ):
+ if self.config.get(
+ 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte'
+ ) != str(int(
+ float(self.lineEditMaxAcceptableTotalDifficulty.text()) *
+ defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
+ ):
+ # the user changed the max acceptable total difficulty
+ acceptableDifficultyChanged = True
+ self.config.set(
+ 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte',
+ str(int(
+ float(self.lineEditMaxAcceptableTotalDifficulty.text()) *
+ defaults.networkDefaultProofOfWorkNonceTrialsPerByte))
+ )
+ if (
+ float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1 or
+ float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0
+ ):
+ if self.config.get(
+ 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes'
+ ) != str(int(
+ float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) *
+ defaults.networkDefaultPayloadLengthExtraBytes)
+ ):
+ # the user changed the max acceptable small message difficulty
+ acceptableDifficultyChanged = True
+ self.config.set(
+ 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes',
+ str(int(
+ float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) *
+ defaults.networkDefaultPayloadLengthExtraBytes))
+ )
+ if acceptableDifficultyChanged:
+ # It might now be possible to send msgs which were previously
+ # marked as toodifficult. Let us change them to 'msgqueued'.
+ # The singleWorker will try to send them and will again mark
+ # them as toodifficult if the receiver's required difficulty
+ # is still higher than we are willing to do.
+ sqlExecute(
+ "UPDATE sent SET status='msgqueued'"
+ " WHERE status='toodifficult'")
+ queues.workerQueue.put(('sendmessage', ''))
+
+ # UI setting to stop trying to send messages after X days/months
+ # I'm open to changing this UI to something else if someone has a better idea.
+ if self.lineEditDays.text() == '' and self.lineEditMonths.text() == '':
+ # We need to handle this special case. Bitmessage has its
+ # default behavior. The input is blank/blank
+ self.config.set('bitmessagesettings', 'stopresendingafterxdays', '')
+ self.config.set('bitmessagesettings', 'stopresendingafterxmonths', '')
+ shared.maximumLengthOfTimeToBotherResendingMessages = float('inf')
+
+ try:
+ days = float(self.lineEditDays.text())
+ except ValueError:
+ self.lineEditDays.setText("0")
+ days = 0.0
+ try:
+ months = float(self.lineEditMonths.text())
+ except ValueError:
+ self.lineEditMonths.setText("0")
+ months = 0.0
+
+ if days >= 0 and months >= 0:
+ shared.maximumLengthOfTimeToBotherResendingMessages = \
+ days * 24 * 60 * 60 + months * 60 * 60 * 24 * 365 / 12
+ if shared.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(
+ self,
+ _translate("MainWindow", "Will not resend ever"),
+ _translate(
+ "MainWindow",
+ "Note that the time limit you entered is less"
+ " than the amount of time Bitmessage waits for"
+ " the first resend attempt therefore your"
+ " messages will never be resent.")
+ )
+ self.config.set(
+ 'bitmessagesettings', 'stopresendingafterxdays', '0')
+ self.config.set(
+ 'bitmessagesettings', 'stopresendingafterxmonths', '0')
+ shared.maximumLengthOfTimeToBotherResendingMessages = 0.0
+ else:
+ self.config.set(
+ 'bitmessagesettings', 'stopresendingafterxdays', str(days))
+ self.config.set(
+ 'bitmessagesettings', 'stopresendingafterxmonths',
+ str(months))
+
+ self.config.save()
+
+ self.parent.updateStartOnLogon()
+
+ if (
+ state.appdata != paths.lookupExeFolder() and
+ self.checkBoxPortableMode.isChecked()
+ ):
+ # If we are NOT using portable mode now but the user selected
+ # that we should...
+ # Write the keys.dat file to disk in the new location
+ sqlStoredProcedure('movemessagstoprog')
+ with open(paths.lookupExeFolder() + 'keys.dat', 'wb') as configfile:
+ self.config.write(configfile)
+ # Write the knownnodes.dat file to disk in the new location
+ knownnodes.saveKnownNodes(paths.lookupExeFolder())
+ os.remove(state.appdata + 'keys.dat')
+ os.remove(state.appdata + 'knownnodes.dat')
+ previousAppdataLocation = state.appdata
+ state.appdata = paths.lookupExeFolder()
+ debug.resetLogging()
+ try:
+ os.remove(previousAppdataLocation + 'debug.log')
+ os.remove(previousAppdataLocation + 'debug.log.1')
+ except:
+ pass
+
+ if (
+ state.appdata == paths.lookupExeFolder() and
+ not self.checkBoxPortableMode.isChecked()
+ ):
+ # If we ARE using portable mode now but the user selected
+ # that we shouldn't...
+ state.appdata = paths.lookupAppdataFolder()
+ if not os.path.exists(state.appdata):
+ os.makedirs(state.appdata)
+ sqlStoredProcedure('movemessagstoappdata')
+ # Write the keys.dat file to disk in the new location
+ self.config.save()
+ # Write the knownnodes.dat file to disk in the new location
+ knownnodes.saveKnownNodes(state.appdata)
+ os.remove(paths.lookupExeFolder() + 'keys.dat')
+ os.remove(paths.lookupExeFolder() + 'knownnodes.dat')
+ debug.resetLogging()
+ try:
+ os.remove(paths.lookupExeFolder() + 'debug.log')
+ os.remove(paths.lookupExeFolder() + 'debug.log.1')
+ except:
+ pass
diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui
index 4aeba3ce..307c06c2 100644
--- a/src/bitmessageqt/settings.ui
+++ b/src/bitmessageqt/settings.ui
@@ -37,6 +37,18 @@
User Interface
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
-
@@ -44,20 +56,43 @@
- -
-
-
- Start Bitmessage in the tray (don't show main window)
+
-
+
+
+ Tray
+
+
-
+
+
+ Start Bitmessage in the tray (don't show main window)
+
+
+
+ -
+
+
+ Minimize to tray
+
+
+ true
+
+
+
+ -
+
+
+ Close to tray
+
+
+
+
-
-
+
- Minimize to tray
-
-
- true
+ Hide connection notifications
@@ -117,90 +152,15 @@
Interface Language
-
- -
-
+
+
-
+
100
0
-
-
-
- System Settings
-
-
- -
-
- English
-
-
- -
-
- Esperanto
-
-
- -
-
- Français
-
-
- -
-
- Deutsch
-
-
- -
-
- Español
-
-
- -
-
- русский
-
-
- -
-
- Norsk
-
-
- -
-
- العربية
-
-
- -
-
- 简体中文
-
-
- -
-
- 日本語
-
-
- -
-
- Nederlands
-
-
- -
-
- Česky
-
-
- -
-
- Pirate English
-
-
- -
-
- Other (set in keys.dat)
-
-
@@ -213,6 +173,18 @@
Network Settings
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
-
@@ -220,26 +192,13 @@
-
-
-
- Qt::Horizontal
-
-
-
- 125
- 20
-
-
-
-
- -
Listen for connections on port:
- -
+
-
@@ -249,6 +208,26 @@
+ -
+
+
+ UPnP
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
@@ -466,6 +445,18 @@
Demanded difficulty
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
-
@@ -594,6 +585,18 @@
Max acceptable difficulty
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
-
@@ -698,6 +701,33 @@
+ -
+
+
-
+
+
+ Hardware GPU acceleration (OpenCL):
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
@@ -705,6 +735,18 @@
Namecoin integration
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
-
@@ -888,6 +930,18 @@
Resends Expire
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
-
@@ -912,91 +966,69 @@
-
-
+
231
75
+
+
-
-
-
- 10
- 20
- 101
- 20
-
-
Give up after
-
+
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
+
+
+ -
-
-
- 30
- 40
- 80
- 16
-
-
and
-
+
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- 113
- 20
- 51
- 20
-
-
+
+ -
+
+
+
+ 55
+ 100
+
+
-
-
-
- 113
- 40
- 51
- 20
-
-
+
+ -
+
+
+
+ 55
+ 100
+
+
-
-
-
- 169
- 23
- 61
- 16
-
-
+
+ -
+
days
-
-
-
- 170
- 41
- 71
- 16
-
-
+
+ -
+
months.
+
+
-
@@ -1017,7 +1049,14 @@
-
+
+
+
+ LanguageBox
+ QComboBox
+
+
+
tabWidgetSettings
checkBoxStartOnLogon
@@ -1101,5 +1140,53 @@
+
+ comboBoxProxyType
+ currentIndexChanged(int)
+ settingsDialog
+ comboBoxProxyTypeChanged
+
+
+ 20
+ 20
+
+
+ 20
+ 20
+
+
+
+
+ radioButtonNamecoinNamecoind
+ toggled(bool)
+ settingsDialog
+ namecoinTypeChanged
+
+
+ 20
+ 20
+
+
+ 20
+ 20
+
+
+
+
+ pushButtonNamecoinTest
+ clicked()
+ settingsDialog
+ click_pushButtonNamecoinTest
+
+
+ 20
+ 20
+
+
+ 20
+ 20
+
+
+
From 18392017c63c538f99fa0cf833c8d494b54bd449 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Tue, 20 Nov 2018 16:53:25 +0200
Subject: [PATCH 02/39] Do not propose user to restart Bitmessage
if network settings have changed, drop network connections instead
---
src/bitmessageqt/settings.py | 61 +++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 29 deletions(-)
diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py
index fc96b137..dced52ff 100644
--- a/src/bitmessageqt/settings.py
+++ b/src/bitmessageqt/settings.py
@@ -1,7 +1,7 @@
import os
import sys
-from PyQt4 import QtGui
+from PyQt4 import QtCore, QtGui
import debug
import defaults
@@ -29,6 +29,8 @@ class SettingsDialog(QtGui.QDialog):
self.parent = parent
self.firstrun = firstrun
self.config = BMConfigParser()
+ self.net_restart_needed = False
+ self.timer = QtCore.QTimer()
self.lineEditMaxOutboundConnections.setValidator(
QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections))
@@ -98,7 +100,8 @@ class SettingsDialog(QtGui.QDialog):
self.checkBoxSocksListen.setChecked(
config.getboolean('bitmessagesettings', 'sockslisten'))
- proxy_type = config.get('bitmessagesettings', 'socksproxytype')
+ proxy_type = config.safeGet(
+ 'bitmessagesettings', 'socksproxytype', 'none')
if proxy_type == 'none':
self.comboBoxProxyType.setCurrentIndex(0)
self.lineEditSocksHostname.setEnabled(False)
@@ -283,16 +286,11 @@ class SettingsDialog(QtGui.QDialog):
if int(self.config.get('bitmessagesettings', 'port')) != int(
self.lineEditTCPPort.text()):
- if not self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
- QtGui.QMessageBox.about(
- self, _translate("MainWindow", "Restart"),
- _translate(
- "MainWindow",
- "You must restart Bitmessage for the port number"
- " change to take effect.")
- )
self.config.set(
'bitmessagesettings', 'port', str(self.lineEditTCPPort.text()))
+ if not self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
+ self.net_restart_needed = True
+
if self.checkBoxUPnP.isChecked() != self.config.safeGetBoolean(
'bitmessagesettings', 'upnp'):
self.config.set(
@@ -303,32 +301,28 @@ class SettingsDialog(QtGui.QDialog):
upnpThread = upnp.uPnPThread()
upnpThread.start()
+ proxy_type = self.config.safeGet(
+ 'bitmessagesettings', 'socksproxytype', 'none')
if (
- self.config.get('bitmessagesettings', 'socksproxytype') ==
- 'none' and
- self.comboBoxProxyType.currentText()[0:5] == 'SOCKS'
+ proxy_type == 'none' and
+ self.comboBoxProxyType.currentText()[0:5] == 'SOCKS' and
+ shared.statusIconColor != 'red'
):
- if shared.statusIconColor != 'red':
- QtGui.QMessageBox.about(
- self, _translate("MainWindow", "Restart"),
- _translate(
- "MainWindow",
- "Bitmessage will use your proxy from now on but"
- " you may want to manually restart Bitmessage now"
- " to close existing connections (if any).")
- )
+ self.net_restart_needed = True
if (
- self.config.get('bitmessagesettings', 'socksproxytype')[0:5] ==
- 'SOCKS' and self.comboBoxProxyType.currentText()[0:5] != 'SOCKS'
+ proxy_type[0:5] == 'SOCKS' and
+ self.comboBoxProxyType.currentText()[0:5] != 'SOCKS'
):
+ self.net_restart_needed = True
self.parent.statusbar.clearMessage()
# just in case we changed something in the network connectivity
state.resetNetworkProtocolAvailability()
- if self.comboBoxProxyType.currentText()[0:5] == 'SOCKS':
- self.config.set('bitmessagesettings', 'socksproxytype', str(
- self.comboBoxProxyType.currentText()))
- else:
- self.config.set('bitmessagesettings', 'socksproxytype', 'none')
+ self.config.set(
+ 'bitmessagesettings', 'socksproxytype',
+ str(self.comboBoxProxyType.currentText())
+ if self.comboBoxProxyType.currentText()[0:5] == 'SOCKS'
+ else 'none'
+ )
self.config.set('bitmessagesettings', 'socksauthentication', str(
self.checkBoxAuthentication.isChecked()))
self.config.set('bitmessagesettings', 'sockshostname', str(
@@ -495,6 +489,15 @@ class SettingsDialog(QtGui.QDialog):
self.config.save()
+ if self.net_restart_needed:
+ self.net_restart_needed = False
+ self.config.set('bitmessagesettings', 'dontconnect', 'true')
+ self.timer.singleShot(
+ 5000, lambda:
+ self.config.remove_option(
+ 'bitmessagesettings', 'dontconnect')
+ )
+
self.parent.updateStartOnLogon()
if (
From df66277e2d19e835ef6f9f0adb8a4e3a9d616769 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Tue, 30 Jul 2019 11:16:48 +0300
Subject: [PATCH 03/39] state.resetNetworkProtocolAvailability() is obsolete
---
src/bitmessageqt/settings.py | 3 +--
src/state.py | 11 -----------
2 files changed, 1 insertion(+), 13 deletions(-)
diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py
index dced52ff..4273257e 100644
--- a/src/bitmessageqt/settings.py
+++ b/src/bitmessageqt/settings.py
@@ -315,8 +315,7 @@ class SettingsDialog(QtGui.QDialog):
):
self.net_restart_needed = True
self.parent.statusbar.clearMessage()
- # just in case we changed something in the network connectivity
- state.resetNetworkProtocolAvailability()
+
self.config.set(
'bitmessagesettings', 'socksproxytype',
str(self.comboBoxProxyType.currentText())
diff --git a/src/state.py b/src/state.py
index 2cbc3a7c..3e051edf 100644
--- a/src/state.py
+++ b/src/state.py
@@ -9,9 +9,6 @@ extPort = None
# for Tor hidden service
socksIP = None
-# Network protocols availability, initialised below
-networkProtocolAvailability = None
-
appdata = '' # holds the location of the application data storage directory
# Set to 1 by the doCleanShutdown function.
@@ -54,14 +51,6 @@ discoveredPeers = {}
Peer = collections.namedtuple('Peer', ['host', 'port'])
-
-def resetNetworkProtocolAvailability():
- global networkProtocolAvailability
- networkProtocolAvailability = {'IPv4': None, 'IPv6': None, 'onion': None}
-
-
-resetNetworkProtocolAvailability()
-
dandelion = 0
testmode = False
From 24ae91ad0ad438b07f8000cc18d08f03ccc16509 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Thu, 5 Sep 2019 18:31:16 +0300
Subject: [PATCH 04/39] Set dontconnect temporary, completely avoiding saving
---
src/bitmessageqt/settings.py | 6 +++---
src/bmconfigparser.py | 13 +++++++++++++
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py
index 4273257e..513f285b 100644
--- a/src/bitmessageqt/settings.py
+++ b/src/bitmessageqt/settings.py
@@ -490,11 +490,11 @@ class SettingsDialog(QtGui.QDialog):
if self.net_restart_needed:
self.net_restart_needed = False
- self.config.set('bitmessagesettings', 'dontconnect', 'true')
+ self.config.setTemp('bitmessagesettings', 'dontconnect', 'true')
self.timer.singleShot(
5000, lambda:
- self.config.remove_option(
- 'bitmessagesettings', 'dontconnect')
+ self.config.setTemp(
+ 'bitmessagesettings', 'dontconnect', 'false')
)
self.parent.updateStartOnLogon()
diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py
index 1ee64e94..726d32eb 100644
--- a/src/bmconfigparser.py
+++ b/src/bmconfigparser.py
@@ -46,6 +46,8 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
"""Singleton class inherited from ConfigParser.SafeConfigParser
with additional methods specific to bitmessage config."""
+ _temp = {}
+
def set(self, section, option, value=None):
if self._optcre is self.OPTCRE or value:
if not isinstance(value, basestring):
@@ -59,6 +61,10 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
if section == "bitmessagesettings" and option == "timeformat":
return ConfigParser.ConfigParser.get(
self, section, option, raw, variables)
+ try:
+ return self._temp[section][option]
+ except KeyError:
+ pass
return ConfigParser.ConfigParser.get(
self, section, option, True, variables)
except ConfigParser.InterpolationError:
@@ -70,6 +76,13 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
except (KeyError, ValueError, AttributeError):
raise e
+ def setTemp(self, section, option, value=None):
+ """Temporary set option to value, not saving."""
+ try:
+ self._temp[section][option] = value
+ except KeyError:
+ self._temp[section] = {option: value}
+
def safeGetBoolean(self, section, field):
try:
return self.getboolean(section, field)
From 4c7f9487e251a70cd173debdd4a287f71f3be0e2 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 15:12:40 +0530
Subject: [PATCH 05/39] init file fixes for pylint
---
src/bitmessagecurses/__init__.py | 800 +++++++++++++++++++------------
1 file changed, 499 insertions(+), 301 deletions(-)
diff --git a/src/bitmessagecurses/__init__.py b/src/bitmessagecurses/__init__.py
index dc6e8a0c..80dc3f14 100644
--- a/src/bitmessagecurses/__init__.py
+++ b/src/bitmessagecurses/__init__.py
@@ -1,40 +1,40 @@
+"""
+src/bitmessagecurses/__init__.py
+================================
+"""
+
# Copyright (c) 2014 Luke Montalvo
# This file adds a alternative commandline interface, feel free to critique and fork
-#
+#
# This has only been tested on Arch Linux and Linux Mint
# Dependencies:
# * from python2-pip
# * python2-pythondialog
# * dialog
+import ConfigParser
+import curses
import os
import sys
-import StringIO
-from textwrap import *
-
import time
-from time import strftime, localtime
+from textwrap import fill
from threading import Timer
-import curses
-import dialog
-from dialog import Dialog
-from helper_sql import *
-from helper_ackPayload import genAckPayload
-
-from addresses import *
-import ConfigParser
+from addresses import addBMIfNotPresent, decodeAddress
from bmconfigparser import BMConfigParser
+from dialog import Dialog
+from helper_ackPayload import genAckPayload
+from helper_sql import sqlExecute, sqlQuery
from inventory import Inventory
import l10n
+import network.stats
from pyelliptic.openssl import OpenSSL
import queues
import shared
import shutdown
-import network.stats
-quit = False
+quit = False # pylint: disable=redefined-builtin
menutab = 1
menu = ["Inbox", "Send", "Sent", "Your Identities", "Subscriptions", "Address Book", "Blacklist", "Network Status"]
naptime = 100
@@ -60,156 +60,189 @@ bwtype = "black"
BROADCAST_STR = "[Broadcast subscribers]"
-class printLog:
+
+class printLog: # pylint: disable=no-self-use, no-init, old-style-class
+ """Printing logs"""
+
def write(self, output):
+ # pylint: disable=global-statement
global log
log += output
+
def flush(self):
pass
-class errLog:
+
+
+class errLog: # pylint: disable=no-self-use, no-init, old-style-class
+ """Error logs"""
def write(self, output):
+ # pylint: disable=global-statement
global log
- log += "!"+output
+ log += "!" + output
+
def flush(self):
pass
+
+
printlog = printLog()
errlog = errLog()
def cpair(a):
+ """Color pairs"""
r = curses.color_pair(a)
- if r not in range(1, curses.COLOR_PAIRS-1):
+ if r not in range(1, curses.COLOR_PAIRS - 1):
r = curses.color_pair(0)
return r
+
+
def ascii(s):
+ """ASCII values"""
r = ""
for c in s:
if ord(c) in range(128):
r += c
return r
+
def drawmenu(stdscr):
+ """Creating menu's"""
menustr = " "
- for i in range(0, len(menu)):
- if menutab == i+1:
+ for i, _ in enumerate(menu):
+ if menutab == i + 1:
menustr = menustr[:-1]
menustr += "["
- menustr += str(i+1)+menu[i]
- if menutab == i+1:
+ menustr += str(i + 1) + menu[i]
+ if menutab == i + 1:
menustr += "] "
- elif i != len(menu)-1:
+ elif i != len(menu) - 1:
menustr += " "
stdscr.addstr(2, 5, menustr, curses.A_UNDERLINE)
+
def set_background_title(d, title):
+ """Setting background title"""
try:
d.set_background_title(title)
except:
d.add_persistent_args(("--backtitle", title))
+
def scrollbox(d, text, height=None, width=None):
+ """Setting scroll box"""
try:
- d.scrollbox(text, height, width, exit_label = "Continue")
+ d.scrollbox(text, height, width, exit_label="Continue")
except:
- d.msgbox(text, height or 0, width or 0, ok_label = "Continue")
+ d.msgbox(text, height or 0, width or 0, ok_label="Continue")
+
def resetlookups():
- global inventorydata
+ """Reset the Inventory Lookups"""
+ global inventorydata # pylint: disable=global-statement
inventorydata = Inventory().numberOfInventoryLookupsPerformed
Inventory().numberOfInventoryLookupsPerformed = 0
Timer(1, resetlookups, ()).start()
-def drawtab(stdscr):
- if menutab in range(1, len(menu)+1):
- if menutab == 1: # Inbox
+
+
+def drawtab(stdscr): # pylint: disable=too-many-branches, too-many-statements
+ """Method for drawing different tabs"""
+ if menutab in range(1, len(menu) + 1):
+ if menutab == 1: # Inbox
stdscr.addstr(3, 5, "To", curses.A_BOLD)
stdscr.addstr(3, 40, "From", curses.A_BOLD)
stdscr.addstr(3, 80, "Subject", curses.A_BOLD)
stdscr.addstr(3, 120, "Time Received", curses.A_BOLD)
stdscr.hline(4, 5, '-', 121)
- for i, item in enumerate(inbox[max(min(len(inbox)-curses.LINES+6, inboxcur-5), 0):]):
- if 6+i < curses.LINES:
+ for i, item in enumerate(inbox[max(min(len(inbox) - curses.LINES + 6, inboxcur - 5), 0):]):
+ if 6 + i < curses.LINES:
a = 0
- if i == inboxcur - max(min(len(inbox)-curses.LINES+6, inboxcur-5), 0): # Highlight current address
+ if i == inboxcur - max(min(len(inbox) - curses.LINES + 6, inboxcur - 5), 0):
+ # Highlight current address
a = a | curses.A_REVERSE
- if item[7] == False: # If not read, highlight
+ if item[7] is False: # If not read, highlight
a = a | curses.A_BOLD
- stdscr.addstr(5+i, 5, item[1][:34], a)
- stdscr.addstr(5+i, 40, item[3][:39], a)
- stdscr.addstr(5+i, 80, item[5][:39], a)
- stdscr.addstr(5+i, 120, item[6][:39], a)
- elif menutab == 3: # Sent
+ stdscr.addstr(5 + i, 5, item[1][:34], a)
+ stdscr.addstr(5 + i, 40, item[3][:39], a)
+ stdscr.addstr(5 + i, 80, item[5][:39], a)
+ stdscr.addstr(5 + i, 120, item[6][:39], a)
+ elif menutab == 3: # Sent
stdscr.addstr(3, 5, "To", curses.A_BOLD)
stdscr.addstr(3, 40, "From", curses.A_BOLD)
stdscr.addstr(3, 80, "Subject", curses.A_BOLD)
stdscr.addstr(3, 120, "Status", curses.A_BOLD)
stdscr.hline(4, 5, '-', 121)
- for i, item in enumerate(sentbox[max(min(len(sentbox)-curses.LINES+6, sentcur-5), 0):]):
- if 6+i < curses.LINES:
+ for i, item in enumerate(sentbox[max(min(len(sentbox) - curses.LINES + 6, sentcur - 5), 0):]):
+ if 6 + i < curses.LINES:
a = 0
- if i == sentcur - max(min(len(sentbox)-curses.LINES+6, sentcur-5), 0): # Highlight current address
+ if i == sentcur - max(min(len(sentbox) - curses.LINES + 6, sentcur - 5), 0):
+ # Highlight current address
a = a | curses.A_REVERSE
- stdscr.addstr(5+i, 5, item[0][:34], a)
- stdscr.addstr(5+i, 40, item[2][:39], a)
- stdscr.addstr(5+i, 80, item[4][:39], a)
- stdscr.addstr(5+i, 120, item[5][:39], a)
- elif menutab == 2 or menutab == 4: # Send or Identities
+ stdscr.addstr(5 + i, 5, item[0][:34], a)
+ stdscr.addstr(5 + i, 40, item[2][:39], a)
+ stdscr.addstr(5 + i, 80, item[4][:39], a)
+ stdscr.addstr(5 + i, 120, item[5][:39], a)
+ elif menutab == 2 or menutab == 4: # Send or Identities
stdscr.addstr(3, 5, "Label", curses.A_BOLD)
stdscr.addstr(3, 40, "Address", curses.A_BOLD)
stdscr.addstr(3, 80, "Stream", curses.A_BOLD)
stdscr.hline(4, 5, '-', 81)
- for i, item in enumerate(addresses[max(min(len(addresses)-curses.LINES+6, addrcur-5), 0):]):
- if 6+i < curses.LINES:
+ for i, item in enumerate(addresses[max(min(len(addresses) - curses.LINES + 6, addrcur - 5), 0):]):
+ if 6 + i < curses.LINES:
a = 0
- if i == addrcur - max(min(len(addresses)-curses.LINES+6, addrcur-5), 0): # Highlight current address
+ if i == addrcur - max(min(len(addresses) - curses.LINES + 6, addrcur - 5), 0):
+ # Highlight current address
a = a | curses.A_REVERSE
- if item[1] == True and item[3] not in [8,9]: # Embolden enabled, non-special addresses
+ if item[1] and item[3] not in [8, 9]: # Embolden enabled, non-special addresses
a = a | curses.A_BOLD
- stdscr.addstr(5+i, 5, item[0][:34], a)
- stdscr.addstr(5+i, 40, item[2][:39], cpair(item[3]) | a)
- stdscr.addstr(5+i, 80, str(1)[:39], a)
- elif menutab == 5: # Subscriptions
+ stdscr.addstr(5 + i, 5, item[0][:34], a)
+ stdscr.addstr(5 + i, 40, item[2][:39], cpair(item[3]) | a)
+ stdscr.addstr(5 + i, 80, str(1)[:39], a)
+ elif menutab == 5: # Subscriptions
stdscr.addstr(3, 5, "Label", curses.A_BOLD)
stdscr.addstr(3, 80, "Address", curses.A_BOLD)
stdscr.addstr(3, 120, "Enabled", curses.A_BOLD)
stdscr.hline(4, 5, '-', 121)
- for i, item in enumerate(subscriptions[max(min(len(subscriptions)-curses.LINES+6, subcur-5), 0):]):
- if 6+i < curses.LINES:
+ for i, item in enumerate(subscriptions[max(min(len(subscriptions) - curses.LINES + 6, subcur - 5), 0):]):
+ if 6 + i < curses.LINES:
a = 0
- if i == subcur - max(min(len(subscriptions)-curses.LINES+6, subcur-5), 0): # Highlight current address
+ if i == subcur - max(min(len(subscriptions) - curses.LINES + 6, subcur - 5), 0):
+ # Highlight current address
a = a | curses.A_REVERSE
- if item[2] == True: # Embolden enabled subscriptions
+ if item[2]: # Embolden enabled subscriptions
a = a | curses.A_BOLD
- stdscr.addstr(5+i, 5, item[0][:74], a)
- stdscr.addstr(5+i, 80, item[1][:39], a)
- stdscr.addstr(5+i, 120, str(item[2]), a)
- elif menutab == 6: # Address book
+ stdscr.addstr(5 + i, 5, item[0][:74], a)
+ stdscr.addstr(5 + i, 80, item[1][:39], a)
+ stdscr.addstr(5 + i, 120, str(item[2]), a)
+ elif menutab == 6: # Address book
stdscr.addstr(3, 5, "Label", curses.A_BOLD)
stdscr.addstr(3, 40, "Address", curses.A_BOLD)
stdscr.hline(4, 5, '-', 41)
- for i, item in enumerate(addrbook[max(min(len(addrbook)-curses.LINES+6, abookcur-5), 0):]):
- if 6+i < curses.LINES:
+ for i, item in enumerate(addrbook[max(min(len(addrbook) - curses.LINES + 6, abookcur - 5), 0):]):
+ if 6 + i < curses.LINES:
a = 0
- if i == abookcur - max(min(len(addrbook)-curses.LINES+6, abookcur-5), 0): # Highlight current address
+ if i == abookcur - max(min(len(addrbook) - curses.LINES + 6, abookcur - 5), 0):
+ # Highlight current address
a = a | curses.A_REVERSE
- stdscr.addstr(5+i, 5, item[0][:34], a)
- stdscr.addstr(5+i, 40, item[1][:39], a)
- elif menutab == 7: # Blacklist
- stdscr.addstr(3, 5, "Type: "+bwtype)
+ stdscr.addstr(5 + i, 5, item[0][:34], a)
+ stdscr.addstr(5 + i, 40, item[1][:39], a)
+ elif menutab == 7: # Blacklist
+ stdscr.addstr(3, 5, "Type: " + bwtype)
stdscr.addstr(4, 5, "Label", curses.A_BOLD)
stdscr.addstr(4, 80, "Address", curses.A_BOLD)
stdscr.addstr(4, 120, "Enabled", curses.A_BOLD)
stdscr.hline(5, 5, '-', 121)
- for i, item in enumerate(blacklist[max(min(len(blacklist)-curses.LINES+6, blackcur-5), 0):]):
- if 7+i < curses.LINES:
+ for i, item in enumerate(blacklist[max(min(len(blacklist) - curses.LINES + 6, blackcur - 5), 0):]):
+ if 7 + i < curses.LINES:
a = 0
- if i == blackcur - max(min(len(blacklist)-curses.LINES+6, blackcur-5), 0): # Highlight current address
+ if i == blackcur - max(min(len(blacklist) - curses.LINES + 6, blackcur - 5), 0):
+ # Highlight current address
a = a | curses.A_REVERSE
- if item[2] == True: # Embolden enabled subscriptions
+ if item[2]: # Embolden enabled subscriptions
a = a | curses.A_BOLD
- stdscr.addstr(6+i, 5, item[0][:74], a)
- stdscr.addstr(6+i, 80, item[1][:39], a)
- stdscr.addstr(6+i, 120, str(item[2]), a)
- elif menutab == 8: # Network status
+ stdscr.addstr(6 + i, 5, item[0][:74], a)
+ stdscr.addstr(6 + i, 80, item[1][:39], a)
+ stdscr.addstr(6 + i, 120, str(item[2]), a)
+ elif menutab == 8: # Network status
# Connection data
connected_hosts = network.stats.connectedHostsList()
stdscr.addstr(
@@ -228,51 +261,63 @@ def drawtab(stdscr):
for i, item in enumerate(streamcount):
if i < 4:
if i == 0:
- stdscr.addstr(8+i, 6, "?")
+ stdscr.addstr(8 + i, 6, "?")
else:
- stdscr.addstr(8+i, 6, str(i))
- stdscr.addstr(8+i, 18, str(item).ljust(2))
-
+ stdscr.addstr(8 + i, 6, str(i))
+ stdscr.addstr(8 + i, 18, str(item).ljust(2))
+
# Uptime and processing data
- stdscr.addstr(6, 35, "Since startup on "+l10n.formatTimestamp(startuptime, False))
- stdscr.addstr(7, 40, "Processed "+str(shared.numberOfMessagesProcessed).ljust(4)+" person-to-person messages.")
- stdscr.addstr(8, 40, "Processed "+str(shared.numberOfBroadcastsProcessed).ljust(4)+" broadcast messages.")
- stdscr.addstr(9, 40, "Processed "+str(shared.numberOfPubkeysProcessed).ljust(4)+" public keys.")
-
+ stdscr.addstr(6, 35, "Since startup on " + l10n.formatTimestamp(startuptime, False))
+ stdscr.addstr(7, 40, "Processed " + str(
+ shared.numberOfMessagesProcessed).ljust(4) + " person-to-person messages.")
+ stdscr.addstr(8, 40, "Processed " + str(
+ shared.numberOfBroadcastsProcessed).ljust(4) + " broadcast messages.")
+ stdscr.addstr(9, 40, "Processed " + str(
+ shared.numberOfPubkeysProcessed).ljust(4) + " public keys.")
+
# Inventory data
- stdscr.addstr(11, 35, "Inventory lookups per second: "+str(inventorydata).ljust(3))
-
+ stdscr.addstr(11, 35, "Inventory lookups per second: " + str(inventorydata).ljust(3))
+
# Log
stdscr.addstr(13, 6, "Log", curses.A_BOLD)
n = log.count('\n')
if n > 0:
l = log.split('\n')
if n > 512:
- del l[:(n-256)]
+ del l[:(n - 256)]
logpad.erase()
n = len(l)
for i, item in enumerate(l):
a = 0
- if len(item) > 0 and item[0] == '!':
+ if item and item[0] == '!':
a = curses.color_pair(1)
item = item[1:]
logpad.addstr(i, 0, item, a)
- logpad.refresh(n-curses.LINES+2, 0, 14, 6, curses.LINES-2, curses.COLS-7)
+ logpad.refresh(n - curses.LINES + 2, 0, 14, 6, curses.LINES - 2, curses.COLS - 7)
stdscr.refresh()
+
def redraw(stdscr):
+ """Redraw menu"""
stdscr.erase()
stdscr.border()
drawmenu(stdscr)
stdscr.refresh()
+
+
def dialogreset(stdscr):
+ """Resetting dialogue"""
stdscr.clear()
stdscr.keypad(1)
curses.curs_set(0)
+
+
+# pylint: disable=too-many-branches, too-many-statements
def handlech(c, stdscr):
+ # pylint: disable=redefined-outer-name, too-many-nested-blocks, too-many-locals, global-statement
if c != curses.ERR:
global inboxcur, addrcur, sentcur, subcur, abookcur, blackcur
- if c in range(256):
+ if c in range(256):
if chr(c) in '12345678':
global menutab
menutab = int(chr(c))
@@ -284,17 +329,27 @@ def handlech(c, stdscr):
d = Dialog(dialog="dialog")
if menutab == 1:
set_background_title(d, "Inbox Message Dialog Box")
- r, t = d.menu("Do what with \""+inbox[inboxcur][5]+"\" from \""+inbox[inboxcur][3]+"\"?",
- choices=[("1", "View message"),
+ r, t = d.menu(
+ "Do what with \"" + inbox[inboxcur][5] + "\" from \"" + inbox[inboxcur][3] + "\"?",
+ choices=[
+ ("1", "View message"),
("2", "Mark message as unread"),
("3", "Reply"),
("4", "Add sender to Address Book"),
("5", "Save message as text file"),
("6", "Move to trash")])
if r == d.DIALOG_OK:
- if t == "1": # View
- set_background_title(d, "\""+inbox[inboxcur][5]+"\" from \""+inbox[inboxcur][3]+"\" to \""+inbox[inboxcur][1]+"\"")
- data = ""
+ if t == "1": # View
+ set_background_title(
+ d,
+ "\"" +
+ inbox[inboxcur][5] +
+ "\" from \"" +
+ inbox[inboxcur][3] +
+ "\" to \"" +
+ inbox[inboxcur][1] +
+ "\"")
+ data = "" # pyint: disable=redefined-outer-name
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", inbox[inboxcur][0])
if ret != []:
for row in ret:
@@ -302,16 +357,16 @@ def handlech(c, stdscr):
data = shared.fixPotentiallyInvalidUTF8Data(data)
msg = ""
for i, item in enumerate(data.split("\n")):
- msg += fill(item, replace_whitespace=False)+"\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])
inbox[inboxcur][7] = 1
else:
scrollbox(d, unicode("Could not fetch message."))
- elif t == "2": # Mark unread
+ elif t == "2": # Mark unread
sqlExecute("UPDATE inbox SET read=0 WHERE msgid=?", inbox[inboxcur][0])
inbox[inboxcur][7] = 0
- elif t == "3": # Reply
+ elif t == "3": # Reply
curses.curs_set(1)
m = inbox[inboxcur]
fromaddr = m[4]
@@ -320,29 +375,31 @@ def handlech(c, stdscr):
if fromaddr == item[2] and item[3] != 0:
ischan = True
break
- if not addresses[i][1]:
- scrollbox(d, unicode("Sending address disabled, please either enable it or choose a different address."))
+ if not addresses[i][1]: # pylint: disable=undefined-loop-variable
+ scrollbox(d, unicode(
+ "Sending address disabled, please either enable it"
+ "or choose a different address."))
return
toaddr = m[2]
if ischan:
toaddr = fromaddr
-
+
subject = m[5]
if not m[5][:4] == "Re: ":
- subject = "Re: "+m[5]
+ subject = "Re: " + m[5]
body = ""
ret = sqlQuery("SELECT message FROM inbox WHERE msgid=?", m[0])
if ret != []:
body = "\n\n------------------------------------------------------\n"
for row in ret:
body, = row
-
+
sendMessage(fromaddr, toaddr, ischan, subject, body, True)
dialogreset(stdscr)
- elif t == "4": # Add to Address Book
+ elif t == "4": # Add to Address Book
addr = inbox[inboxcur][4]
- if addr not in [item[1] for i,item in enumerate(addrbook)]:
- r, t = d.inputbox("Label for address \""+addr+"\"")
+ 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:
label = t
sqlExecute("INSERT INTO addressbook VALUES (?,?)", label, addr)
@@ -352,61 +409,85 @@ def handlech(c, stdscr):
addrbook.reverse()
else:
scrollbox(d, unicode("The selected address is already in the Address Book."))
- elif t == "5": # Save message
- set_background_title(d, "Save \""+inbox[inboxcur][5]+"\" as text file")
- r, t = d.inputbox("Filename", init=inbox[inboxcur][5]+".txt")
+ elif t == "5": # Save message
+ set_background_title(d, "Save \"" + inbox[inboxcur][5] + "\" as text file")
+ 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])
if ret != []:
for row in ret:
msg, = row
- fh = open(t, "a") # Open in append mode just in case
+ 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
+ elif t == "6": # Move to trash
sqlExecute("UPDATE inbox SET folder='trash' WHERE msgid=?", inbox[inboxcur][0])
del inbox[inboxcur]
- scrollbox(d, unicode("Message moved to trash. There is no interface to view your trash, \nbut the message is still on disk if you are desperate to recover it."))
+ scrollbox(d, unicode(
+ "Message moved to trash. There is no interface to view your trash,"
+ " \nbut the message is still on disk if you are desperate to recover it."))
elif menutab == 2:
a = ""
- if addresses[addrcur][3] != 0: # if current address is a chan
+ if addresses[addrcur][3] != 0: # if current address is a chan
a = addresses[addrcur][2]
sendMessage(addresses[addrcur][2], a)
elif menutab == 3:
set_background_title(d, "Sent Messages Dialog Box")
- r, t = d.menu("Do what with \""+sentbox[sentcur][4]+"\" to \""+sentbox[sentcur][0]+"\"?",
- choices=[("1", "View message"),
+ r, t = d.menu(
+ "Do what with \"" + sentbox[sentcur][4] + "\" to \"" + sentbox[sentcur][0] + "\"?",
+ choices=[
+ ("1", "View message"),
("2", "Move to trash")])
if r == d.DIALOG_OK:
- if t == "1": # View
- set_background_title(d, "\""+sentbox[sentcur][4]+"\" from \""+sentbox[sentcur][3]+"\" to \""+sentbox[sentcur][1]+"\"")
+ if t == "1": # View
+ set_background_title(
+ d,
+ "\"" +
+ sentbox[sentcur][4] +
+ "\" from \"" +
+ sentbox[sentcur][3] +
+ "\" to \"" +
+ sentbox[sentcur][1] +
+ "\"")
data = ""
- ret = sqlQuery("SELECT message FROM sent WHERE subject=? AND ackdata=?", sentbox[sentcur][4], sentbox[sentcur][6])
+ ret = sqlQuery(
+ "SELECT message FROM sent WHERE subject=? AND ackdata=?",
+ sentbox[sentcur][4],
+ sentbox[sentcur][6])
if ret != []:
for row in ret:
data, = row
data = shared.fixPotentiallyInvalidUTF8Data(data)
msg = ""
for i, item in enumerate(data.split("\n")):
- msg += fill(item, replace_whitespace=False)+"\n"
+ msg += fill(item, replace_whitespace=False) + "\n"
scrollbox(d, unicode(ascii(msg)), 30, 80)
else:
scrollbox(d, unicode("Could not fetch message."))
- elif t == "2": # Move to trash
- sqlExecute("UPDATE sent SET folder='trash' WHERE subject=? AND ackdata=?", sentbox[sentcur][4], sentbox[sentcur][6])
+ elif t == "2": # Move to trash
+ sqlExecute(
+ "UPDATE sent SET folder='trash' WHERE subject=? AND ackdata=?",
+ sentbox[sentcur][4],
+ sentbox[sentcur][6])
del sentbox[sentcur]
- scrollbox(d, unicode("Message moved to trash. There is no interface to view your trash, \nbut the message is still on disk if you are desperate to recover it."))
+ scrollbox(d, unicode(
+ "Message moved to trash. There is no interface to view your trash"
+ " \nbut the message is still on disk if you are desperate to recover it."))
elif menutab == 4:
set_background_title(d, "Your Identities Dialog Box")
if len(addresses) <= addrcur:
- r, t = d.menu("Do what with addresses?",
- choices=[("1", "Create new address")])
+ r, t = d.menu(
+ "Do what with addresses?",
+ choices=[
+ ("1", "Create new address")])
else:
- r, t = d.menu("Do what with \""+addresses[addrcur][0]+"\" : \""+addresses[addrcur][2]+"\"?",
- choices=[("1", "Create new address"),
+ r, t = d.menu(
+ "Do what with \"" + addresses[addrcur][0] + "\" : \"" + addresses[addrcur][2] + "\"?",
+ choices=[
+ ("1", "Create new address"),
("2", "Send a message from this address"),
("3", "Rename"),
("4", "Enable"),
@@ -414,31 +495,41 @@ def handlech(c, stdscr):
("6", "Delete"),
("7", "Special address behavior")])
if r == d.DIALOG_OK:
- if t == "1": # Create new address
+ if t == "1": # Create new address
set_background_title(d, "Create new address")
- scrollbox(d, unicode("Here you may generate as many addresses as you like.\n"
- "Indeed, creating and abandoning addresses is encouraged.\n"
- "Deterministic addresses have several pros and cons:\n"
- "\nPros:\n"
- " * You can recreate your addresses on any computer from memory\n"
- " * You need not worry about backing up your keys.dat file as long as you \n can remember your passphrase\n"
- "Cons:\n"
- " * You must remember (or write down) your passphrase in order to recreate \n your keys if they are lost\n"
- " * You must also remember the address version and stream numbers\n"
- " * If you choose a weak passphrase someone may be able to brute-force it \n and then send and receive messages as you"))
- r, t = d.menu("Choose an address generation technique",
- choices=[("1", "Use a random number generator"),
+ scrollbox(
+ d, unicode(
+ "Here you may generate as many addresses as you like.\n"
+ "Indeed, creating and abandoning addresses is encouraged.\n"
+ "Deterministic addresses have several pros and cons:\n"
+ "\nPros:\n"
+ " * You can recreate your addresses on any computer from memory\n"
+ " * You need not worry about backing up your keys.dat file as long as you"
+ " \n can remember your passphrase\n"
+ "Cons:\n"
+ " * You must remember (or write down) your passphrase in order to recreate"
+ " \n your keys if they are lost\n"
+ " * You must also remember the address version and stream numbers\n"
+ " * If you choose a weak passphrase someone may be able to brute-force it"
+ " \n and then send and receive messages as you"))
+ r, t = d.menu(
+ "Choose an address generation technique",
+ choices=[
+ ("1", "Use a random number generator"),
("2", "Use a passphrase")])
if r == d.DIALOG_OK:
if t == "1":
set_background_title(d, "Randomly generate address")
r, t = d.inputbox("Label (not shown to anyone except you)")
label = ""
- if r == d.DIALOG_OK and len(t) > 0:
+ if r == d.DIALOG_OK and t:
label = t
- r, t = d.menu("Choose a stream",
- choices=[("1", "Use the most available stream"),("", "(Best if this is the first of many addresses you will create)"),
- ("2", "Use the same stream as an existing address"),("", "(Saves you some bandwidth and processing power)")])
+ r, t = d.menu(
+ "Choose a stream",
+ choices=[("1", "Use the most available stream"),
+ ("", "(Best if this is the first of many addresses you will create)"),
+ ("2", "Use the same stream as an existing address"),
+ ("", "(Saves you some bandwidth and processing power)")])
if r == d.DIALOG_OK:
if t == "1":
stream = 1
@@ -450,42 +541,69 @@ def handlech(c, stdscr):
if r == d.DIALOG_OK:
stream = decodeAddress(addrs[int(t)][1])[2]
shorten = False
- r, t = d.checklist("Miscellaneous options",
- choices=[("1", "Spend time shortening the address", 1 if shorten else 0)])
+ r, t = d.checklist(
+ "Miscellaneous options",
+ choices=[(
+ "1",
+ "Spend time shortening the address",
+ 1 if shorten else 0)])
if r == d.DIALOG_OK and "1" in t:
shorten = True
- queues.addressGeneratorQueue.put(("createRandomAddress", 4, stream, label, 1, "", shorten))
+ queues.addressGeneratorQueue.put((
+ "createRandomAddress",
+ 4,
+ stream,
+ label,
+ 1,
+ "",
+ shorten))
elif t == "2":
set_background_title(d, "Make deterministic addresses")
- r, t = d.passwordform("Enter passphrase",
- [("Passphrase", 1, 1, "", 2, 1, 64, 128),
- ("Confirm passphrase", 3, 1, "", 4, 1, 64, 128)],
+ r, t = d.passwordform(
+ "Enter passphrase",
+ [
+ ("Passphrase", 1, 1, "", 2, 1, 64, 128),
+ ("Confirm passphrase", 3, 1, "", 4, 1, 64, 128)],
form_height=4, insecure=True)
if r == d.DIALOG_OK:
if t[0] == t[1]:
passphrase = t[0]
- r, t = d.rangebox("Number of addresses to generate",
- width=48, min=1, max=99, init=8)
+ r, t = d.rangebox(
+ "Number of addresses to generate",
+ width=48,
+ min=1,
+ max=99,
+ init=8)
if r == d.DIALOG_OK:
number = t
stream = 1
shorten = False
- r, t = d.checklist("Miscellaneous options",
- choices=[("1", "Spend time shortening the address", 1 if shorten else 0)])
+ r, t = d.checklist(
+ "Miscellaneous options",
+ choices=[(
+ "1",
+ "Spend time shortening the address",
+ 1 if shorten else 0)])
if r == d.DIALOG_OK and "1" in t:
shorten = True
- scrollbox(d, unicode("In addition to your passphrase, be sure to remember the following numbers:\n"
- "\n * Address version number: "+str(4)+"\n"
- " * Stream number: "+str(stream)))
- queues.addressGeneratorQueue.put(('createDeterministicAddresses', 4, stream, "unused deterministic address", number, str(passphrase), shorten))
+ scrollbox(
+ d, unicode(
+ "In addition to your passphrase, be sure to remember the"
+ " following numbers:\n"
+ "\n * Address version number: " + str(4) + "\n"
+ " * Stream number: " + str(stream)))
+ queues.addressGeneratorQueue.put(
+ ('createDeterministicAddresses', 4, stream,
+ "unused deterministic address", number,
+ str(passphrase), shorten))
else:
scrollbox(d, unicode("Passphrases do not match"))
- elif t == "2": # Send a message
+ elif t == "2": # Send a message
a = ""
- if addresses[addrcur][3] != 0: # if current address is a chan
+ if addresses[addrcur][3] != 0: # if current address is a chan
a = addresses[addrcur][2]
sendMessage(addresses[addrcur][2], a)
- elif t == "3": # Rename address label
+ elif t == "3": # Rename address label
a = addresses[addrcur][2]
label = addresses[addrcur][0]
r, t = d.inputbox("New address label", init=label)
@@ -495,72 +613,79 @@ def handlech(c, stdscr):
# Write config
BMConfigParser().save()
addresses[addrcur][0] = label
- elif t == "4": # Enable address
+ elif t == "4": # Enable address
a = addresses[addrcur][2]
- BMConfigParser().set(a, "enabled", "true") # Set config
+ BMConfigParser().set(a, "enabled", "true") # Set config
# Write config
BMConfigParser().save()
# Change color
if BMConfigParser().safeGetBoolean(a, 'chan'):
- addresses[addrcur][3] = 9 # orange
+ addresses[addrcur][3] = 9 # orange
elif BMConfigParser().safeGetBoolean(a, 'mailinglist'):
- addresses[addrcur][3] = 5 # magenta
+ addresses[addrcur][3] = 5 # magenta
else:
- addresses[addrcur][3] = 0 # black
+ addresses[addrcur][3] = 0 # black
addresses[addrcur][1] = True
- shared.reloadMyAddressHashes() # Reload address hashes
- elif t == "5": # Disable address
+ shared.reloadMyAddressHashes() # Reload address hashes
+ elif t == "5": # Disable address
a = addresses[addrcur][2]
- BMConfigParser().set(a, "enabled", "false") # Set config
- addresses[addrcur][3] = 8 # Set color to gray
+ BMConfigParser().set(a, "enabled", "false") # Set config
+ addresses[addrcur][3] = 8 # Set color to gray
# Write config
BMConfigParser().save()
addresses[addrcur][1] = False
- shared.reloadMyAddressHashes() # Reload address hashes
- elif t == "6": # Delete address
+ shared.reloadMyAddressHashes() # Reload address hashes
+ elif t == "6": # Delete address
r, t = d.inputbox("Type in \"I want to delete this address\"", width=50)
if r == d.DIALOG_OK and t == "I want to delete this address":
- BMConfigParser().remove_section(addresses[addrcur][2])
- BMConfigParser().save()
- del addresses[addrcur]
- elif t == "7": # Special address behavior
+ BMConfigParser().remove_section(addresses[addrcur][2])
+ BMConfigParser().save()
+ del addresses[addrcur]
+ elif t == "7": # Special address behavior
a = addresses[addrcur][2]
set_background_title(d, "Special address behavior")
if BMConfigParser().safeGetBoolean(a, "chan"):
- scrollbox(d, unicode("This is a chan address. You cannot use it as a pseudo-mailing list."))
+ scrollbox(d, unicode(
+ "This is a chan address. You cannot use it as a pseudo-mailing list."))
else:
m = BMConfigParser().safeGetBoolean(a, "mailinglist")
- r, t = d.radiolist("Select address behavior",
- choices=[("1", "Behave as a normal address", not m),
+ r, t = d.radiolist(
+ "Select address behavior",
+ choices=[
+ ("1", "Behave as a normal address", not m),
("2", "Behave as a pseudo-mailing-list address", m)])
if r == d.DIALOG_OK:
- if t == "1" and m == True:
+ if t == "1" and m:
BMConfigParser().set(a, "mailinglist", "false")
if addresses[addrcur][1]:
- addresses[addrcur][3] = 0 # Set color to black
+ addresses[addrcur][3] = 0 # Set color to black
else:
- addresses[addrcur][3] = 8 # Set color to gray
- elif t == "2" and m == False:
+ addresses[addrcur][3] = 8 # Set color to gray
+ elif t == "2" and m is False:
try:
mn = BMConfigParser().get(a, "mailinglistname")
except ConfigParser.NoOptionError:
- mn = ""
+ mn = ""
r, t = d.inputbox("Mailing list name", init=mn)
if r == d.DIALOG_OK:
mn = t
BMConfigParser().set(a, "mailinglist", "true")
BMConfigParser().set(a, "mailinglistname", mn)
- addresses[addrcur][3] = 6 # Set color to magenta
+ addresses[addrcur][3] = 6 # Set color to magenta
# Write config
BMConfigParser().save()
elif menutab == 5:
set_background_title(d, "Subscriptions Dialog Box")
if len(subscriptions) <= subcur:
- r, t = d.menu("Do what with subscription to \""+subscriptions[subcur][0]+"\"?",
- choices=[("1", "Add new subscription")])
+ r, t = d.menu(
+ "Do what with subscription to \"" + subscriptions[subcur][0] + "\"?",
+ choices=[
+ ("1", "Add new subscription")])
else:
- r, t = d.menu("Do what with subscription to \""+subscriptions[subcur][0]+"\"?",
- choices=[("1", "Add new subscription"),
+ r, t = d.menu(
+ "Do what with subscription to \"" + subscriptions[subcur][0] + "\"?",
+ choices=[
+ ("1", "Add new subscription"),
("2", "Delete this subscription"),
("3", "Enable"),
("4", "Disable")])
@@ -581,27 +706,39 @@ def handlech(c, stdscr):
sqlExecute("INSERT INTO subscriptions VALUES (?,?,?)", label, addr, True)
shared.reloadBroadcastSendersForWhichImWatching()
elif t == "2":
- r, t = d.inpuxbox("Type in \"I want to delete this subscription\"")
+ 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])
- shared.reloadBroadcastSendersForWhichImWatching()
- del subscriptions[subcur]
+ sqlExecute(
+ "DELETE FROM subscriptions WHERE label=? AND address=?",
+ subscriptions[subcur][0],
+ 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])
+ sqlExecute(
+ "UPDATE subscriptions SET enabled=1 WHERE label=? AND address=?",
+ subscriptions[subcur][0],
+ 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])
+ sqlExecute(
+ "UPDATE subscriptions SET enabled=0 WHERE label=? AND address=?",
+ subscriptions[subcur][0],
+ subscriptions[subcur][1])
shared.reloadBroadcastSendersForWhichImWatching()
subscriptions[subcur][2] = False
elif menutab == 6:
set_background_title(d, "Address Book Dialog Box")
if len(addrbook) <= abookcur:
- r, t = d.menu("Do what with addressbook?",
+ r, t = d.menu(
+ "Do what with addressbook?",
choices=[("3", "Add new address to Address Book")])
else:
- r, t = d.menu("Do what with \""+addrbook[abookcur][0]+"\" : \""+addrbook[abookcur][1]+"\"",
- choices=[("1", "Send a message to this address"),
+ r, t = d.menu(
+ "Do what with \"" + addrbook[abookcur][0] + "\" : \"" + addrbook[abookcur][1] + "\"",
+ choices=[
+ ("1", "Send a message to this address"),
("2", "Subscribe to this address"),
("3", "Add new address to Address Book"),
("4", "Delete this address")])
@@ -623,8 +760,8 @@ def handlech(c, stdscr):
r, t = d.inputbox("Input new address")
if r == d.DIALOG_OK:
addr = t
- if addr not in [item[1] for i,item in enumerate(addrbook)]:
- r, t = d.inputbox("Label for address \""+addr+"\"")
+ 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)
# Prepend entry
@@ -636,25 +773,39 @@ def handlech(c, stdscr):
elif t == "4":
r, t = d.inputbox("Type in \"I want to delete this Address Book entry\"")
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])
+ sqlExecute(
+ "DELETE FROM addressbook WHERE label=? AND address=?",
+ addrbook[abookcur][0],
+ addrbook[abookcur][1])
del addrbook[abookcur]
elif menutab == 7:
set_background_title(d, "Blacklist Dialog Box")
- r, t = d.menu("Do what with \""+blacklist[blackcur][0]+"\" : \""+blacklist[blackcur][1]+"\"?",
- choices=[("1", "Delete"),
+ r, t = d.menu(
+ "Do what with \"" + blacklist[blackcur][0] + "\" : \"" + blacklist[blackcur][1] + "\"?",
+ choices=[
+ ("1", "Delete"),
("2", "Enable"),
("3", "Disable")])
if r == d.DIALOG_OK:
if t == "1":
r, t = d.inputbox("Type in \"I want to delete this Blacklist entry\"")
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])
+ sqlExecute(
+ "DELETE FROM blacklist WHERE label=? AND address=?",
+ blacklist[blackcur][0],
+ 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])
+ sqlExecute(
+ "UPDATE blacklist SET enabled=1 WHERE label=? AND address=?",
+ blacklist[blackcur][0],
+ 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])
+ elif t == "3":
+ sqlExecute(
+ "UPDATE blacklist SET enabled=0 WHERE label=? AND address=?",
+ blacklist[blackcur][0],
+ blacklist[blackcur][1])
blacklist[blackcur][2] = False
dialogreset(stdscr)
else:
@@ -672,17 +823,17 @@ def handlech(c, stdscr):
if menutab == 7 and blackcur > 0:
blackcur -= 1
elif c == curses.KEY_DOWN:
- if menutab == 1 and inboxcur < len(inbox)-1:
+ if menutab == 1 and inboxcur < len(inbox) - 1:
inboxcur += 1
- if (menutab == 2 or menutab == 4) and addrcur < len(addresses)-1:
+ if (menutab == 2 or menutab == 4) and addrcur < len(addresses) - 1:
addrcur += 1
- if menutab == 3 and sentcur < len(sentbox)-1:
+ if menutab == 3 and sentcur < len(sentbox) - 1:
sentcur += 1
- if menutab == 5 and subcur < len(subscriptions)-1:
+ if menutab == 5 and subcur < len(subscriptions) - 1:
subcur += 1
- if menutab == 6 and abookcur < len(addrbook)-1:
+ if menutab == 6 and abookcur < len(addrbook) - 1:
abookcur += 1
- if menutab == 7 and blackcur < len(blacklist)-1:
+ if menutab == 7 and blackcur < len(blacklist) - 1:
blackcur += 1
elif c == curses.KEY_HOME:
if menutab == 1:
@@ -699,38 +850,47 @@ def handlech(c, stdscr):
blackcur = 0
elif c == curses.KEY_END:
if menutab == 1:
- inboxcur = len(inbox)-1
+ inboxcur = len(inbox) - 1
if menutab == 2 or menutab == 4:
- addrcur = len(addresses)-1
+ addrcur = len(addresses) - 1
if menutab == 3:
- sentcur = len(sentbox)-1
+ sentcur = len(sentbox) - 1
if menutab == 5:
- subcur = len(subscriptions)-1
+ subcur = len(subscriptions) - 1
if menutab == 6:
- abookcur = len(addrbook)-1
+ abookcur = len(addrbook) - 1
if menutab == 7:
- blackcur = len(blackcur)-1
+ blackcur = len(blackcur) - 1
redraw(stdscr)
+
+
+# pylint: disable=too-many-locals, too-many-arguments
def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=False):
+ """Method for message sending"""
if sender == "":
return
d = Dialog(dialog="dialog")
set_background_title(d, "Send a message")
if recv == "":
- r, t = d.inputbox("Recipient address (Cancel to load from the Address Book or leave blank to broadcast)", 10, 60)
+ r, t = d.inputbox(
+ "Recipient address (Cancel to load from the Address Book or leave blank to broadcast)",
+ 10,
+ 60)
if r != d.DIALOG_OK:
- global menutab
+ global menutab # pylint: disable=global-statement
menutab = 6
return
recv = t
- if broadcast == None and sender != recv:
- r, t = d.radiolist("How to send the message?",
- choices=[("1", "Send to one or more specific people", 1),
+ if broadcast is None and sender != recv:
+ r, t = d.radiolist(
+ "How to send the message?",
+ choices=[
+ ("1", "Send to one or more specific people", 1),
("2", "Broadcast to everyone who is subscribed to your address", 0)])
if r != d.DIALOG_OK:
return
broadcast = False
- if t == "2": # Broadcast
+ if t == "2": # Broadcast
broadcast = True
if subject == "" or reply:
r, t = d.inputbox("Message subject", width=60, init=subject)
@@ -748,9 +908,10 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
recvlist = []
for i, item in enumerate(recv.replace(",", ";").split(";")):
recvlist.append(item.strip())
- list(set(recvlist)) # Remove exact duplicates
+ list(set(recvlist)) # Remove exact duplicates
for addr in recvlist:
if addr != "":
+ # pylint: disable=redefined-outer-name
status, version, stream, ripe = decodeAddress(addr)
if status != "success":
set_background_title(d, "Recipient address error")
@@ -762,13 +923,17 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
elif status == "invalidcharacters":
err += "The address contains invalid characters."
elif status == "versiontoohigh":
- err += "The address version is too high. Either you need to upgrade your Bitmessage software or your acquaintance is doing something clever."
+ err += ("The address version is too high. Either you need to upgrade your Bitmessage software"
+ " or your acquaintance is doing something clever.")
elif status == "ripetooshort":
- err += "Some data encoded in the address is too short. There might be something wrong with the software of your acquaintance."
+ err += ("Some data encoded in the address is too short. There might be something wrong with"
+ " the software of your acquaintance.")
elif status == "ripetoolong":
- err += "Some data encoded in the address is too long. There might be something wrong with the software of your acquaintance."
+ err += ("Some data encoded in the address is too long. There might be something wrong with"
+ " the software of your acquaintance.")
elif status == "varintmalformed":
- err += "Some data encoded in the address is malformed. There might be something wrong with the software of your acquaintance."
+ err += ("Some data encoded in the address is malformed. There might be something wrong with"
+ " the software of your acquaintance.")
else:
err += "It is unknown what is wrong with the address."
scrollbox(d, unicode(err))
@@ -776,17 +941,24 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
addr = addBMIfNotPresent(addr)
if version > 4 or version <= 1:
set_background_title(d, "Recipient address error")
- scrollbox(d, unicode("Could not understand version number " + version + "of address" + addr + "."))
+ scrollbox(d, unicode(
+ "Could not understand version number " +
+ version +
+ "of address" +
+ addr +
+ "."))
continue
if stream > 1 or stream == 0:
set_background_title(d, "Recipient address error")
- scrollbox(d, unicode("Bitmessage currently only supports stream numbers of 1, unlike as requested for address " + addr + "."))
+ scrollbox(d, unicode(
+ "Bitmessage currently only supports stream numbers of 1,"
+ "unlike as requested for address " + addr + "."))
continue
if not network.stats.connectedHostsList():
set_background_title(d, "Not connected warning")
scrollbox(d, unicode("Because you are not currently connected to the network, "))
stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel')
- ackdata = genAckPayload(streamNumber, stealthLevel)
+ ackdata = genAckPayload(decodeAddress(addr)[2], stealthLevel)
sqlExecute(
"INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
"",
@@ -796,22 +968,22 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
subject,
body,
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.
+ int(time.time()), # sentTime (this will never change)
+ int(time.time()), # lastActionTime
+ 0, # sleepTill time. This will get set when the POW gets done.
"msgqueued",
- 0, # retryNumber
+ 0, # retryNumber
"sent",
- 2, # encodingType
+ 2, # encodingType
BMConfigParser().getint('bitmessagesettings', 'ttl'))
queues.workerQueue.put(("sendmessage", addr))
- else: # Broadcast
+ else: # Broadcast
if recv == "":
set_background_title(d, "Empty sender error")
scrollbox(d, unicode("You must specify an address to send the message from."))
else:
# dummy ackdata, no need for stealth
- ackdata = genAckPayload(streamNumber, 0)
+ ackdata = genAckPayload(decodeAddress(addr)[2], 0)
recv = BROADCAST_STR
ripe = ""
sqlExecute(
@@ -823,21 +995,24 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
subject,
body,
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.
+ int(time.time()), # sentTime (this will never change)
+ int(time.time()), # lastActionTime
+ 0, # sleepTill time. This will get set when the POW gets done.
"broadcastqueued",
- 0, # retryNumber
- "sent", # folder
- 2, # encodingType
+ 0, # retryNumber
+ "sent", # folder
+ 2, # encodingType
BMConfigParser().getint('bitmessagesettings', 'ttl'))
queues.workerQueue.put(('sendbroadcast', ''))
+
+# pylint: disable=redefined-outer-name, too-many-locals
def loadInbox():
+ """Load the list of messages"""
sys.stdout = sys.__stdout__
- print("Loading inbox messages...")
+ print "Loading inbox messages..."
sys.stdout = printlog
-
+
where = "toaddress || fromaddress || subject || message"
what = "%%"
ret = sqlQuery("""SELECT msgid, toaddress, fromaddress, subject, received, read
@@ -847,7 +1022,7 @@ def loadInbox():
for row in ret:
msgid, toaddr, fromaddr, subject, received, read = row
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
-
+
# Set label for to address
try:
if toaddr == BROADCAST_STR:
@@ -859,17 +1034,17 @@ def loadInbox():
if tolabel == "":
tolabel = toaddr
tolabel = shared.fixPotentiallyInvalidUTF8Data(tolabel)
-
+
# Set label for from address
fromlabel = ""
if BMConfigParser().has_section(fromaddr):
fromlabel = BMConfigParser().get(fromaddr, "label")
- if fromlabel == "": # Check Address Book
+ if fromlabel == "": # Check Address Book
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", fromaddr)
if qr != []:
for r in qr:
fromlabel, = r
- if fromlabel == "": # Check Subscriptions
+ if fromlabel == "": # Check Subscriptions
qr = sqlQuery("SELECT label FROM subscriptions WHERE address=?", fromaddr)
if qr != []:
for r in qr:
@@ -877,16 +1052,19 @@ def loadInbox():
if fromlabel == "":
fromlabel = fromaddr
fromlabel = shared.fixPotentiallyInvalidUTF8Data(fromlabel)
-
+
# Load into array
- inbox.append([msgid, tolabel, toaddr, fromlabel, fromaddr, subject,
- l10n.formatTimestamp(received, False), read])
+ inbox.append([msgid, tolabel, toaddr, fromlabel, fromaddr, subject, l10n.formatTimestamp(
+ received, False), read])
inbox.reverse()
+
+
def loadSent():
+ """Load the messages that sent"""
sys.stdout = sys.__stdout__
- print("Loading sent messages...")
+ print "Loading sent messages..."
sys.stdout = printlog
-
+
where = "toaddress || fromaddress || subject || message"
what = "%%"
ret = sqlQuery("""SELECT toaddress, fromaddress, subject, status, ackdata, lastactiontime
@@ -896,7 +1074,7 @@ def loadSent():
for row in ret:
toaddr, fromaddr, subject, status, ackdata, lastactiontime = row
subject = ascii(shared.fixPotentiallyInvalidUTF8Data(subject))
-
+
# Set label for to address
tolabel = ""
qr = sqlQuery("SELECT label FROM addressbook WHERE address=?", toaddr)
@@ -913,14 +1091,14 @@ def loadSent():
tolabel = BMConfigParser().get(toaddr, "label")
if tolabel == "":
tolabel = toaddr
-
+
# Set label for from address
fromlabel = ""
if BMConfigParser().has_section(fromaddr):
fromlabel = BMConfigParser().get(fromaddr, "label")
if fromlabel == "":
fromlabel = fromaddr
-
+
# Set status string
if status == "awaitingpubkey":
statstr = "Waiting for their public key. Will request it again soon"
@@ -930,20 +1108,20 @@ def loadSent():
statstr = "Message queued"
elif status == "msgsent":
t = l10n.formatTimestamp(lastactiontime, False)
- statstr = "Message sent at "+t+".Waiting for acknowledgement."
+ statstr = "Message sent at " + t + ".Waiting for acknowledgement."
elif status == "msgsentnoackexpected":
t = l10n.formatTimestamp(lastactiontime, False)
- statstr = "Message sent at "+t+"."
+ statstr = "Message sent at " + t + "."
elif status == "doingmsgpow":
statstr = "The proof of work required to send the message has been queued."
elif status == "ackreceived":
t = l10n.formatTimestamp(lastactiontime, False)
- statstr = "Acknowledgment of the message received at "+t+"."
+ statstr = "Acknowledgment of the message received at " + t + "."
elif status == "broadcastqueued":
statstr = "Broadcast queued."
elif status == "broadcastsent":
t = l10n.formatTimestamp(lastactiontime, False)
- statstr = "Broadcast sent at "+t+"."
+ statstr = "Broadcast sent at " + t + "."
elif status == "forcepow":
statstr = "Forced difficulty override. Message will start sending soon."
elif status == "badkey":
@@ -952,31 +1130,47 @@ def loadSent():
statstr = "Error: The work demanded by the recipient is more difficult than you are willing to do."
else:
t = l10n.formatTimestamp(lastactiontime, False)
- statstr = "Unknown status "+status+" at "+t+"."
-
+ statstr = "Unknown status " + status + " at " + t + "."
+
# Load into array
- sentbox.append([tolabel, toaddr, fromlabel, fromaddr, subject, statstr, ackdata,
+ sentbox.append([
+ tolabel,
+ toaddr,
+ fromlabel,
+ fromaddr,
+ subject,
+ statstr,
+ ackdata,
l10n.formatTimestamp(lastactiontime, False)])
sentbox.reverse()
+
+
def loadAddrBook():
+ """Load address book"""
sys.stdout = sys.__stdout__
- print("Loading address book...")
+ print "Loading address book..."
sys.stdout = printlog
-
+
ret = sqlQuery("SELECT label, address FROM addressbook")
for row in ret:
label, addr = row
label = shared.fixPotentiallyInvalidUTF8Data(label)
addrbook.append([label, addr])
addrbook.reverse()
+
+
def loadSubscriptions():
+ """Load subscription functionality"""
ret = sqlQuery("SELECT label, address, enabled FROM subscriptions")
for row in ret:
label, address, enabled = row
subscriptions.append([label, address, enabled])
subscriptions.reverse()
+
+
def loadBlackWhiteList():
- global bwtype
+ """load black/white list"""
+ global bwtype # pylint: disable=global-statement
bwtype = BMConfigParser().get("bitmessagesettings", "blackwhitelist")
if bwtype == "black":
ret = sqlQuery("SELECT label, address, enabled FROM blacklist")
@@ -987,51 +1181,53 @@ def loadBlackWhiteList():
blacklist.append([label, address, enabled])
blacklist.reverse()
+
def runwrapper():
sys.stdout = printlog
- #sys.stderr = errlog
-
+ # sys.stderr = errlog
+
# Load messages from database
loadInbox()
loadSent()
loadAddrBook()
loadSubscriptions()
loadBlackWhiteList()
-
+
stdscr = curses.initscr()
-
- global logpad
+
+ global logpad # pylint: disable=global-statement
logpad = curses.newpad(1024, curses.COLS)
-
+
stdscr.nodelay(0)
curses.curs_set(0)
stdscr.timeout(1000)
-
+
curses.wrapper(run)
doShutdown()
+
def run(stdscr):
# Schedule inventory lookup data
resetlookups()
-
+
# Init color pairs
if curses.has_colors():
- curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK) # red
- curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK) # green
- curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) # yellow
- curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK) # blue
- curses.init_pair(5, curses.COLOR_MAGENTA, curses.COLOR_BLACK) # magenta
- curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK) # cyan
- curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK) # white
+ curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK) # red
+ curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK) # green
+ curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) # yellow
+ curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK) # blue
+ curses.init_pair(5, curses.COLOR_MAGENTA, curses.COLOR_BLACK) # magenta
+ curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK) # cyan
+ curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK) # white
if curses.can_change_color():
- curses.init_color(8, 500, 500, 500) # gray
+ curses.init_color(8, 500, 500, 500) # gray
curses.init_pair(8, 8, 0)
- curses.init_color(9, 844, 465, 0) # orange
+ curses.init_color(9, 844, 465, 0) # orange
curses.init_pair(9, 9, 0)
else:
- curses.init_pair(8, curses.COLOR_WHITE, curses.COLOR_BLACK) # grayish
- curses.init_pair(9, curses.COLOR_YELLOW, curses.COLOR_BLACK) # orangish
-
+ curses.init_pair(8, curses.COLOR_WHITE, curses.COLOR_BLACK) # grayish
+ curses.init_pair(9, curses.COLOR_YELLOW, curses.COLOR_BLACK) # orangish
+
# Init list of address in 'Your Identities' tab
configSections = BMConfigParser().addresses()
for addressInKeysFile in configSections:
@@ -1039,27 +1235,29 @@ def run(stdscr):
addresses.append([BMConfigParser().get(addressInKeysFile, "label"), isEnabled, addressInKeysFile])
# Set address color
if not isEnabled:
- addresses[len(addresses)-1].append(8) # gray
+ addresses[len(addresses) - 1].append(8) # gray
elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'chan'):
- addresses[len(addresses)-1].append(9) # orange
+ addresses[len(addresses) - 1].append(9) # orange
elif BMConfigParser().safeGetBoolean(addressInKeysFile, 'mailinglist'):
- addresses[len(addresses)-1].append(5) # magenta
+ addresses[len(addresses) - 1].append(5) # magenta
else:
- addresses[len(addresses)-1].append(0) # black
+ addresses[len(addresses) - 1].append(0) # black
addresses.reverse()
-
+
stdscr.clear()
redraw(stdscr)
- while quit == False:
+ while quit is False:
drawtab(stdscr)
handlech(stdscr.getch(), stdscr)
+
def doShutdown():
+ """Shutting the app down"""
sys.stdout = sys.__stdout__
- print("Shutting down...")
+ print "Shutting down..."
sys.stdout = printlog
shutdown.doCleanShutdown()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
-
- os._exit(0)
+
+ os._exit(0) # pylint: disable=protected-access
From 7d0e23e31ae7bcac700e7b3d78f58c3cd8f4b353 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Mon, 23 Sep 2019 17:53:59 +0300
Subject: [PATCH 06/39] Delete from addressbook only by address (Fixes: #1484)
---
src/bitmessageqt/__init__.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py
index 94c00e38..00b0f4e5 100644
--- a/src/bitmessageqt/__init__.py
+++ b/src/bitmessageqt/__init__.py
@@ -3191,8 +3191,7 @@ class MyForm(settingsmixin.SMainWindow):
0].row()
item = self.ui.tableWidgetAddressBook.item(currentRow, 0)
sqlExecute(
- 'DELETE FROM addressbook WHERE label=? AND address=?',
- item.label, item.address)
+ 'DELETE FROM addressbook WHERE address=?', item.address)
self.ui.tableWidgetAddressBook.removeRow(currentRow)
self.rerenderMessagelistFromLabels()
self.rerenderMessagelistToLabels()
From 42a89ad3672678985f6fd14d531ffd6c84283717 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Mon, 23 Sep 2019 18:15:31 +0300
Subject: [PATCH 07/39] Delete from addressbook by pressing DEL
---
src/bitmessageqt/__init__.py | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py
index 00b0f4e5..440d36b2 100644
--- a/src/bitmessageqt/__init__.py
+++ b/src/bitmessageqt/__init__.py
@@ -781,6 +781,9 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.treeWidgetSubscriptions.keyPressEvent = self.treeWidgetKeyPressEvent
self.ui.treeWidgetChans.keyPressEvent = self.treeWidgetKeyPressEvent
+ # Key press in addressbook
+ self.ui.tableWidgetAddressBook.keyPressEvent = self.addressbookKeyPressEvent
+
# Key press in messagelist
self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetKeyPressEvent
self.ui.tableWidgetInboxSubscriptions.keyPressEvent = self.tableWidgetKeyPressEvent
@@ -1450,6 +1453,15 @@ class MyForm(settingsmixin.SMainWindow):
def treeWidgetKeyPressEvent(self, event):
return self.handleKeyPress(event, self.getCurrentTreeWidget())
+ # addressbook
+ def addressbookKeyPressEvent(self, event):
+ """Handle keypress event in addressbook widget"""
+ if event.key() == QtCore.Qt.Key_Delete:
+ self.on_action_AddressBookDelete()
+ else:
+ return QtGui.QTableWidget.keyPressEvent(
+ self.ui.tableWidgetAddressBook, event)
+
# inbox / sent
def tableWidgetKeyPressEvent(self, event):
return self.handleKeyPress(event, self.getCurrentMessagelist())
@@ -1458,11 +1470,12 @@ class MyForm(settingsmixin.SMainWindow):
def textEditKeyPressEvent(self, event):
return self.handleKeyPress(event, self.getCurrentMessageTextedit())
- def handleKeyPress(self, event, focus = None):
+ def handleKeyPress(self, event, focus=None):
+ """This method handles keypress events for all widgets on MyForm"""
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) or isinstance(focus, QtGui.QTableWidget):
if folder == "sent":
self.on_action_SentTrash()
else:
@@ -1498,17 +1511,17 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.lineEditTo.setFocus()
event.ignore()
elif event.key() == QtCore.Qt.Key_F:
- searchline = self.getCurrentSearchLine(retObj = True)
+ searchline = self.getCurrentSearchLine(retObj=True)
if searchline:
searchline.setFocus()
event.ignore()
if not event.isAccepted():
return
- if isinstance (focus, MessageView):
+ if isinstance(focus, MessageView):
return MessageView.keyPressEvent(focus, event)
- elif isinstance (focus, QtGui.QTableWidget):
+ elif isinstance(focus, QtGui.QTableWidget):
return QtGui.QTableWidget.keyPressEvent(focus, event)
- elif isinstance (focus, QtGui.QTreeWidget):
+ elif isinstance(focus, QtGui.QTreeWidget):
return QtGui.QTreeWidget.keyPressEvent(focus, event)
# menu button 'manage keys'
From 0a065670715605e8f676f8a6008b51d6f16f42a0 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Fri, 12 Jul 2019 18:19:06 +0300
Subject: [PATCH 08/39] Connect to bootstrap nodes by name
---
src/knownnodes.py | 10 +++++++++-
src/network/connectionchooser.py | 3 ++-
src/network/connectionpool.py | 3 +--
src/network/socks5.py | 10 ++++------
src/network/tcp.py | 13 ++++++++-----
src/protocol.py | 5 ++++-
6 files changed, 28 insertions(+), 16 deletions(-)
diff --git a/src/knownnodes.py b/src/knownnodes.py
index f4e00b90..6d4629a2 100644
--- a/src/knownnodes.py
+++ b/src/knownnodes.py
@@ -11,7 +11,6 @@ import time
import state
from bmconfigparser import BMConfigParser
from debug import logger
-from helper_bootstrap import dns
knownNodesLock = threading.Lock()
knownNodes = {stream: {} for stream in range(1, 4)}
@@ -123,6 +122,8 @@ def readKnownNodes():
logger.debug(
'Failed to read nodes from knownnodes.dat', exc_info=True)
createDefaultKnownNodes()
+ if BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
+ createDefaultKnownNodes(onion=True)
config = BMConfigParser()
@@ -177,6 +178,13 @@ def trimKnownNodes(recAddrStream=1):
del knownNodes[recAddrStream][oldest]
+def dns():
+ """Add DNS names to knownnodes"""
+ for port in [8080, 8444]:
+ addKnownNode(
+ 1, state.Peer('bootstrap%s.bitmessage.org' % port, port))
+
+
def cleanupKnownNodes():
"""
Cleanup knownnodes: remove old nodes and nodes with low rating
diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py
index e116ec53..4b1565a2 100644
--- a/src/network/connectionchooser.py
+++ b/src/network/connectionchooser.py
@@ -46,7 +46,8 @@ def chooseConnection(stream):
# onion addresses have a higher priority when SOCKS
if peer.host.endswith('.onion') and rating > 0:
rating = 1
- else:
+ # TODO: need better check
+ elif not peer.host.startswith('bootstrap'):
encodedAddr = protocol.encodeHost(peer.host)
# don't connect to local IPs when using SOCKS
if not protocol.checkIPAddress(encodedAddr, False):
diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py
index 461c2b77..05358c28 100644
--- a/src/network/connectionpool.py
+++ b/src/network/connectionpool.py
@@ -8,7 +8,6 @@ import socket
import time
import asyncore_pollchoose as asyncore
-import helper_bootstrap
import helper_random
import knownnodes
import protocol
@@ -185,7 +184,7 @@ class BMConnectionPool(object):
# pylint: disable=too-many-nested-blocks
if spawnConnections:
if not knownnodes.knownNodesActual:
- helper_bootstrap.dns()
+ knownnodes.dns()
if not self.bootstrapped:
self.bootstrapped = True
Proxy.proxy = (
diff --git a/src/network/socks5.py b/src/network/socks5.py
index 86616f30..e0cb7202 100644
--- a/src/network/socks5.py
+++ b/src/network/socks5.py
@@ -8,6 +8,7 @@ src/network/socks5.py
import socket
import struct
+import state
from proxy import GeneralProxyError, Proxy, ProxyError
@@ -160,9 +161,6 @@ class Socks5(Proxy):
class Socks5Connection(Socks5):
"""Child socks5 class used for making outbound connections."""
- def __init__(self, address):
- Socks5.__init__(self, address=address)
-
def state_auth_done(self):
"""Request connection to be made"""
# Now we can request the actual connection
@@ -172,9 +170,9 @@ class Socks5Connection(Socks5):
try:
self.ipaddr = socket.inet_aton(self.destination[0])
self.append_write_buf(chr(0x01).encode() + self.ipaddr)
- except socket.error:
+ except socket.error: # may be IPv6!
# Well it's not an IP number, so it's probably a DNS name.
- if Proxy._remote_dns: # pylint: disable=protected-access
+ if self._remote_dns:
# Resolve remotely
self.ipaddr = None
self.append_write_buf(chr(0x03).encode() + chr(
@@ -202,7 +200,7 @@ class Socks5Resolver(Socks5):
def __init__(self, host):
self.host = host
self.port = 8444
- Socks5.__init__(self, address=(self.host, self.port))
+ Socks5.__init__(self, address=state.Peer(self.host, self.port))
def state_auth_done(self):
"""Perform resolving"""
diff --git a/src/network/tcp.py b/src/network/tcp.py
index 5ebd6a21..9b92cb52 100644
--- a/src/network/tcp.py
+++ b/src/network/tcp.py
@@ -73,11 +73,14 @@ class TCPConnection(BMProto, TLSDispatcher):
logger.debug(
'Connecting to %s:%i',
self.destination.host, self.destination.port)
- encodedAddr = protocol.encodeHost(self.destination.host)
- self.local = all([
- protocol.checkIPAddress(encodedAddr, True),
- not protocol.checkSocksIP(self.destination.host)
- ])
+ try:
+ self.local = (
+ protocol.checkIPAddress(
+ protocol.encodeHost(self.destination.host), True) and
+ not protocol.checkSocksIP(self.destination.host)
+ )
+ except socket.error:
+ pass # it's probably a hostname
ObjectTracker.__init__(self) # pylint: disable=non-parent-init-called
self.bm_proto_reset()
self.set_state("bm_header", expectBytes=protocol.Header.size)
diff --git a/src/protocol.py b/src/protocol.py
index ab81e5e5..1031b950 100644
--- a/src/protocol.py
+++ b/src/protocol.py
@@ -264,7 +264,10 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=
else:
# use first 16 bytes if host data is longer
# for example in case of onion v3 service
- payload += encodeHost(remoteHost)[:16]
+ try:
+ payload += encodeHost(remoteHost)[:16]
+ except socket.error:
+ payload += encodeHost('127.0.0.1')
payload += pack('>H', remotePort) # remote IPv6 and port
# bitflags of the services I offer.
From 4825c5a136450656932f8157677d221c55953d2e Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Sat, 20 Jul 2019 11:41:49 +0300
Subject: [PATCH 09/39] Universal bootstrap procedure for any connection type
---
src/network/connectionpool.py | 29 ++++++++++++++++++++++++++---
src/network/tcp.py | 33 +++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+), 3 deletions(-)
diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py
index 05358c28..1554a585 100644
--- a/src/network/connectionpool.py
+++ b/src/network/connectionpool.py
@@ -18,7 +18,8 @@ from debug import logger
from proxy import Proxy
from singleton import Singleton
from tcp import (
- TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection)
+ bootstrap, Socks4aBMConnection, Socks5BMConnection,
+ TCPConnection, TCPServer)
from udp import UDPSocket
@@ -159,7 +160,28 @@ class BMConnectionPool(object):
udpSocket = UDPSocket(host=bind, announcing=True)
self.udpSockets[udpSocket.listening.host] = udpSocket
- def loop(self): # pylint: disable=too-many-branches, too-many-statements
+ def startBootstrappers(self):
+ """Run the process of resolving bootstrap hostnames"""
+ proxy_type = BMConfigParser().safeGet(
+ 'bitmessagesettings', 'socksproxytype')
+ # A plugins may be added here
+ if not proxy_type or proxy_type == 'none':
+ connection_base = TCPConnection
+ elif proxy_type == 'SOCKS5':
+ connection_base = Socks5BMConnection
+ elif proxy_type == 'SOCKS4a':
+ connection_base = Socks4aBMConnection # FIXME: I cannot test
+ else:
+ # This should never happen because socksproxytype setting
+ # is handled in bitmessagemain before starting the connectionpool
+ return
+
+ bootstrapper = bootstrap(connection_base)
+ port = helper_random.randomchoice([8080, 8444])
+ hostname = 'bootstrap%s.bitmessage.org' % port
+ self.addConnection(bootstrapper(hostname, port))
+
+ def loop(self): # pylint: disable=too-many-branches,too-many-statements
"""Main Connectionpool's loop"""
# defaults to empty loop if outbound connections are maxed
spawnConnections = False
@@ -184,7 +206,8 @@ class BMConnectionPool(object):
# pylint: disable=too-many-nested-blocks
if spawnConnections:
if not knownnodes.knownNodesActual:
- knownnodes.dns()
+ self.startBootstrappers()
+ knownnodes.knownNodesActual = True
if not self.bootstrapped:
self.bootstrapped = True
Proxy.proxy = (
diff --git a/src/network/tcp.py b/src/network/tcp.py
index 9b92cb52..da02df2f 100644
--- a/src/network/tcp.py
+++ b/src/network/tcp.py
@@ -325,6 +325,39 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection):
return True
+def bootstrap(connection_class):
+ """Make bootstrapper class for connection type (connection_class)"""
+ class Bootstrapper(connection_class):
+ """Base class for bootstrappers"""
+ _connection_base = connection_class
+
+ def __init__(self, host, port):
+ self._connection_base.__init__(self, state.Peer(host, port))
+ self.close_reason = self._succeed = False
+
+ def bm_command_addr(self):
+ """
+ Got addr message - the bootstrap succeed.
+ Let BMProto process the addr message and switch state to 'close'
+ """
+ BMProto.bm_command_addr(self)
+ self._succeed = True
+ # pylint: disable=attribute-defined-outside-init
+ self.close_reason = "Thanks for bootstrapping!"
+ self.set_state("close")
+
+ def handle_close(self):
+ """
+ After closing the connection switch knownnodes.knownNodesActual
+ back to False if the bootstrapper failed.
+ """
+ self._connection_base.handle_close(self)
+ if not self._succeed:
+ knownnodes.knownNodesActual = False
+
+ return Bootstrapper
+
+
class TCPServer(AdvancedDispatcher):
"""TCP connection server for Bitmessage protocol"""
From 7215003c6f2ad50ec785392cbf598f5be778d5fa Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Sun, 21 Jul 2019 16:50:49 +0300
Subject: [PATCH 10/39] No DNS resolving in knownnodes
---
src/knownnodes.py | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/src/knownnodes.py b/src/knownnodes.py
index 6d4629a2..2735edbf 100644
--- a/src/knownnodes.py
+++ b/src/knownnodes.py
@@ -191,33 +191,24 @@ def cleanupKnownNodes():
"""
now = int(time.time())
needToWriteKnownNodesToDisk = False
- dns_done = False
- spawnConnections = not BMConfigParser().safeGetBoolean(
- 'bitmessagesettings', 'dontconnect'
- ) and BMConfigParser().safeGetBoolean(
- 'bitmessagesettings', 'sendoutgoingconnections')
with knownNodesLock:
for stream in knownNodes:
if stream not in state.streamsInWhichIAmParticipating:
continue
keys = knownNodes[stream].keys()
- if len(keys) <= 1: # leave at least one node
- if not dns_done and spawnConnections:
- dns()
- dns_done = True
- continue
for node in keys:
+ if len(knownNodes[stream]) <= 1: # leave at least one node
+ break
try:
- # scrap old nodes
- if (now - knownNodes[stream][node]["lastseen"] >
- 2419200): # 28 days
+ age = now - knownNodes[stream][node]["lastseen"]
+ # scrap old nodes (age > 28 days)
+ if age > 2419200:
needToWriteKnownNodesToDisk = True
del knownNodes[stream][node]
continue
- # scrap old nodes with low rating
- if (now - knownNodes[stream][node]["lastseen"] > 10800 and
- knownNodes[stream][node]["rating"] <=
+ # scrap old nodes (age > 3 hours) with low rating
+ if (age > 10800 and knownNodes[stream][node]["rating"] <=
knownNodesForgetRating):
needToWriteKnownNodesToDisk = True
del knownNodes[stream][node]
From bdb09c2d00d2bdbb7ad6ec6076a92f99c246abda Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Mon, 29 Jul 2019 14:37:56 +0300
Subject: [PATCH 11/39] Ignore self node in
connectionchooser.chooseConnection()
---
src/network/connectionchooser.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py
index 4b1565a2..53ce30b7 100644
--- a/src/network/connectionchooser.py
+++ b/src/network/connectionchooser.py
@@ -1,3 +1,4 @@
+# pylint: disable=too-many-branches
import random # nosec
import knownnodes
@@ -38,7 +39,10 @@ def chooseConnection(stream):
for _ in range(50):
peer = random.choice(knownnodes.knownNodes[stream].keys())
try:
- rating = knownnodes.knownNodes[stream][peer]['rating']
+ peer_info = knownnodes.knownNodes[stream][peer]
+ if peer_info.get('self'):
+ continue
+ rating = peer_info["rating"]
except TypeError:
logger.warning('Error in %s', peer)
rating = 0
From bcb29facaac12b47df7adfadeddacde08c9ebfaf Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Mon, 29 Jul 2019 14:19:18 +0300
Subject: [PATCH 12/39] A test for bootstrapping, have problem with
test_tcpconnection ):
---
src/tests/core.py | 48 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 43 insertions(+), 5 deletions(-)
diff --git a/src/tests/core.py b/src/tests/core.py
index a323be83..a9df05fc 100644
--- a/src/tests/core.py
+++ b/src/tests/core.py
@@ -13,9 +13,11 @@ import unittest
import knownnodes
import state
+from bmconfigparser import BMConfigParser
from helper_msgcoding import MsgEncode, MsgDecode
from network import asyncore_pollchoose as asyncore
-from network.tcp import TCPConnection
+from network.connectionpool import BMConnectionPool
+from network.tcp import Socks4aBMConnection, Socks5BMConnection, TCPConnection
from queues import excQueue
knownnodes_file = os.path.join(state.appdata, 'knownnodes.dat')
@@ -80,8 +82,10 @@ class TestCore(unittest.TestCase):
' with no subject!' % e
)
+ @unittest.skip('Bad environment for asyncore.loop')
def test_tcpconnection(self):
"""initial fill script from network.tcp"""
+ BMConfigParser().set('bitmessagesettings', 'dontconnect', 'true')
try:
for peer in (state.Peer("127.0.0.1", 8448),):
direct = TCPConnection(peer)
@@ -91,10 +95,18 @@ class TestCore(unittest.TestCase):
except:
self.fail('Exception in test loop')
- def _wipe_knownnodes(self):
+ @staticmethod
+ def _wipe_knownnodes():
with knownnodes.knownNodesLock:
knownnodes.knownNodes = {stream: {} for stream in range(1, 4)}
+ @staticmethod
+ def _outdate_knownnodes():
+ with knownnodes.knownNodesLock:
+ for nodes in knownnodes.knownNodes.itervalues():
+ for node in nodes.itervalues():
+ node['lastseen'] -= 2419205 # older than 28 days
+
def test_knownnodes_pickle(self):
"""ensure that 3 nodes was imported for each stream"""
pickle_knownnodes()
@@ -117,9 +129,7 @@ class TestCore(unittest.TestCase):
def test_0_cleaner(self):
"""test knownnodes starvation leading to IndexError in Asyncore"""
- for nodes in knownnodes.knownNodes.itervalues():
- for node in nodes.itervalues():
- node['lastseen'] -= 2419205 # older than 28 days
+ self._outdate_knownnodes()
# time.sleep(303) # singleCleaner wakes up every 5 min
knownnodes.cleanupKnownNodes()
while True:
@@ -130,6 +140,34 @@ class TestCore(unittest.TestCase):
if thread == 'Asyncore' and isinstance(exc, IndexError):
self.fail("IndexError because of empty knownNodes!")
+ def test_bootstrap(self):
+ """test bootstrapping"""
+ BMConfigParser().set('bitmessagesettings', 'dontconnect', 'true')
+ self._outdate_knownnodes()
+ knownnodes.cleanupKnownNodes()
+ # it's weird, knownnodes appear empty
+ knownnodes.addKnownNode(1, state.Peer('127.0.0.1', 8444), is_self=True)
+ time.sleep(0.25)
+ BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
+ proxy_type = BMConfigParser().safeGet(
+ 'bitmessagesettings', 'socksproxytype')
+ if proxy_type == 'SOCKS5':
+ connection_base = Socks5BMConnection
+ elif proxy_type == 'SOCKS4a':
+ connection_base = Socks4aBMConnection
+ else:
+ connection_base = TCPConnection
+ _started = time.time()
+ for _ in range(180):
+ time.sleep(1)
+ for peer, con in BMConnectionPool().outboundConnections.iteritems():
+ if not peer.host.startswith('bootstrap'):
+ self.assertIsInstance(con, connection_base)
+ return
+ else: # pylint: disable=useless-else-on-loop
+ self.fail(
+ 'Failed to connect during %s sec' % (time.time() - _started))
+
def run():
"""Starts all tests defined in this module"""
From 6a0c3ae075d787d40e3fb4507393eb46fafe64c8 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Thu, 1 Aug 2019 15:28:32 +0300
Subject: [PATCH 13/39] Remove obsolete helper_bootstrap and bundled SocksiPy
---
setup.py | 1 -
src/helper_bootstrap.py | 84 -------
src/socks/BUGS | 25 ---
src/socks/LICENSE | 22 --
src/socks/README | 201 -----------------
src/socks/__init__.py | 476 ----------------------------------------
6 files changed, 809 deletions(-)
delete mode 100644 src/helper_bootstrap.py
delete mode 100644 src/socks/BUGS
delete mode 100644 src/socks/LICENSE
delete mode 100644 src/socks/README
delete mode 100644 src/socks/__init__.py
diff --git a/setup.py b/setup.py
index 3a9c7a3c..61afa91e 100644
--- a/setup.py
+++ b/setup.py
@@ -70,7 +70,6 @@ if __name__ == "__main__":
'pybitmessage.network',
'pybitmessage.plugins',
'pybitmessage.pyelliptic',
- 'pybitmessage.socks',
'pybitmessage.storage'
]
diff --git a/src/helper_bootstrap.py b/src/helper_bootstrap.py
deleted file mode 100644
index 1710b09c..00000000
--- a/src/helper_bootstrap.py
+++ /dev/null
@@ -1,84 +0,0 @@
-import socket
-
-import knownnodes
-import socks
-import state
-from bmconfigparser import BMConfigParser
-from debug import logger
-
-
-def dns():
- """
- DNS bootstrap. This could be programmed to use the SOCKS proxy to do the
- DNS lookup some day but for now we will just rely on the entries in
- defaultKnownNodes.py. Hopefully either they are up to date or the user
- has run Bitmessage recently without SOCKS turned on and received good
- bootstrap nodes using that method.
- """
-
- def try_add_known_node(stream, addr, port, method=''):
- try:
- socket.inet_aton(addr)
- except (TypeError, socket.error):
- return
- logger.info(
- 'Adding %s to knownNodes based on %s DNS bootstrap method',
- addr, method)
- knownnodes.addKnownNode(stream, state.Peer(addr, port))
-
- proxy_type = BMConfigParser().get('bitmessagesettings', 'socksproxytype')
-
- if proxy_type == 'none':
- for port in [8080, 8444]:
- try:
- for item in socket.getaddrinfo(
- 'bootstrap%s.bitmessage.org' % port, 80):
- try_add_known_node(1, item[4][0], port)
- except:
- logger.error(
- 'bootstrap%s.bitmessage.org DNS bootstrapping failed.',
- port, exc_info=True
- )
- elif proxy_type == 'SOCKS5':
- knownnodes.createDefaultKnownNodes(onion=True)
- logger.debug('Adding default onion knownNodes.')
- for port in [8080, 8444]:
- logger.debug("Resolving %i through SOCKS...", port)
- address_family = socket.AF_INET
- sock = socks.socksocket(address_family, socket.SOCK_STREAM)
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- sock.settimeout(20)
- proxytype = socks.PROXY_TYPE_SOCKS5
- sockshostname = BMConfigParser().get(
- 'bitmessagesettings', 'sockshostname')
- socksport = BMConfigParser().getint(
- 'bitmessagesettings', 'socksport')
- # Do domain name lookups through the proxy;
- # though this setting doesn't really matter since we won't
- # be doing any domain name lookups anyway.
- rdns = True
- if BMConfigParser().getboolean(
- 'bitmessagesettings', 'socksauthentication'):
- socksusername = BMConfigParser().get(
- 'bitmessagesettings', 'socksusername')
- sockspassword = BMConfigParser().get(
- 'bitmessagesettings', 'sockspassword')
- sock.setproxy(
- proxytype, sockshostname, socksport, rdns,
- socksusername, sockspassword)
- else:
- sock.setproxy(
- proxytype, sockshostname, socksport, rdns)
- try:
- ip = sock.resolve("bootstrap" + str(port) + ".bitmessage.org")
- sock.shutdown(socket.SHUT_RDWR)
- sock.close()
- except:
- logger.error("SOCKS DNS resolving failed", exc_info=True)
- else:
- try_add_known_node(1, ip, port, 'SOCKS')
- else:
- logger.info(
- 'DNS bootstrap skipped because the proxy type does not support'
- ' DNS resolution.'
- )
diff --git a/src/socks/BUGS b/src/socks/BUGS
deleted file mode 100644
index fa8ccfad..00000000
--- a/src/socks/BUGS
+++ /dev/null
@@ -1,25 +0,0 @@
-SocksiPy version 1.00
-A Python SOCKS module.
-(C) 2006 Dan-Haim. All rights reserved.
-See LICENSE file for details.
-
-
-KNOWN BUGS AND ISSUES
-----------------------
-
-There are no currently known bugs in this module.
-There are some limits though:
-
-1) Only outgoing connections are supported - This module currently only supports
-outgoing TCP connections, though some servers may support incoming connections
-as well. UDP is not supported either.
-
-2) GSSAPI Socks5 authenticaion is not supported.
-
-
-If you find any new bugs, please contact the author at:
-
-negativeiq@users.sourceforge.net
-
-
-Thank you!
diff --git a/src/socks/LICENSE b/src/socks/LICENSE
deleted file mode 100644
index 04b6b1f3..00000000
--- a/src/socks/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright 2006 Dan-Haim. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-3. Neither the name of Dan Haim nor the names of his contributors may be used
- to endorse or promote products derived from this software without specific
- prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
diff --git a/src/socks/README b/src/socks/README
deleted file mode 100644
index a52f55f3..00000000
--- a/src/socks/README
+++ /dev/null
@@ -1,201 +0,0 @@
-SocksiPy version 1.00
-A Python SOCKS module.
-(C) 2006 Dan-Haim. All rights reserved.
-See LICENSE file for details.
-
-
-WHAT IS A SOCKS PROXY?
-A SOCKS proxy is a proxy server at the TCP level. In other words, it acts as
-a tunnel, relaying all traffic going through it without modifying it.
-SOCKS proxies can be used to relay traffic using any network protocol that
-uses TCP.
-
-WHAT IS SOCKSIPY?
-This Python module allows you to create TCP connections through a SOCKS
-proxy without any special effort.
-
-PROXY COMPATIBILITY
-SocksiPy is compatible with three different types of proxies:
-1. SOCKS Version 4 (Socks4), including the Socks4a extension.
-2. SOCKS Version 5 (Socks5).
-3. HTTP Proxies which support tunneling using the CONNECT method.
-
-SYSTEM REQUIREMENTS
-Being written in Python, SocksiPy can run on any platform that has a Python
-interpreter and TCP/IP support.
-This module has been tested with Python 2.3 and should work with greater versions
-just as well.
-
-
-INSTALLATION
--------------
-
-Simply copy the file "socks.py" to your Python's lib/site-packages directory,
-and you're ready to go.
-
-
-USAGE
-------
-
-First load the socks module with the command:
-
->>> import socks
->>>
-
-The socks module provides a class called "socksocket", which is the base to
-all of the module's functionality.
-The socksocket object has the same initialization parameters as the normal socket
-object to ensure maximal compatibility, however it should be noted that socksocket
-will only function with family being AF_INET and type being SOCK_STREAM.
-Generally, it is best to initialize the socksocket object with no parameters
-
->>> s = socks.socksocket()
->>>
-
-The socksocket object has an interface which is very similiar to socket's (in fact
-the socksocket class is derived from socket) with a few extra methods.
-To select the proxy server you would like to use, use the setproxy method, whose
-syntax is:
-
-setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
-
-Explaination of the parameters:
-
-proxytype - The type of the proxy server. This can be one of three possible
-choices: PROXY_TYPE_SOCKS4, PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP for Socks4,
-Socks5 and HTTP servers respectively.
-
-addr - The IP address or DNS name of the proxy server.
-
-port - The port of the proxy server. Defaults to 1080 for socks and 8080 for http.
-
-rdns - This is a boolean flag than modifies the behavior regarding DNS resolving.
-If it is set to True, DNS resolving will be preformed remotely, on the server.
-If it is set to False, DNS resolving will be preformed locally. Please note that
-setting this to True with Socks4 servers actually use an extension to the protocol,
-called Socks4a, which may not be supported on all servers (Socks5 and http servers
-always support DNS). The default is True.
-
-username - For Socks5 servers, this allows simple username / password authentication
-with the server. For Socks4 servers, this parameter will be sent as the userid.
-This parameter is ignored if an HTTP server is being used. If it is not provided,
-authentication will not be used (servers may accept unauthentication requests).
-
-password - This parameter is valid only for Socks5 servers and specifies the
-respective password for the username provided.
-
-Example of usage:
-
->>> s.setproxy(socks.PROXY_TYPE_SOCKS5,"socks.example.com")
->>>
-
-After the setproxy method has been called, simply call the connect method with the
-traditional parameters to establish a connection through the proxy:
-
->>> s.connect(("www.sourceforge.net",80))
->>>
-
-Connection will take a bit longer to allow negotiation with the proxy server.
-Please note that calling connect without calling setproxy earlier will connect
-without a proxy (just like a regular socket).
-
-Errors: Any errors in the connection process will trigger exceptions. The exception
-may either be generated by the underlying socket layer or may be custom module
-exceptions, whose details follow:
-
-class ProxyError - This is a base exception class. It is not raised directly but
-rather all other exception classes raised by this module are derived from it.
-This allows an easy way to catch all proxy-related errors.
-
-class GeneralProxyError - When thrown, it indicates a problem which does not fall
-into another category. The parameter is a tuple containing an error code and a
-description of the error, from the following list:
-1 - invalid data - This error means that unexpected data has been received from
-the server. The most common reason is that the server specified as the proxy is
-not really a Socks4/Socks5/HTTP proxy, or maybe the proxy type specified is wrong.
-4 - bad proxy type - This will be raised if the type of the proxy supplied to the
-setproxy function was not PROXY_TYPE_SOCKS4/PROXY_TYPE_SOCKS5/PROXY_TYPE_HTTP.
-5 - bad input - This will be raised if the connect method is called with bad input
-parameters.
-
-class Socks5AuthError - This indicates that the connection through a Socks5 server
-failed due to an authentication problem. The parameter is a tuple containing a
-code and a description message according to the following list:
-
-1 - authentication is required - This will happen if you use a Socks5 server which
-requires authentication without providing a username / password at all.
-2 - all offered authentication methods were rejected - This will happen if the proxy
-requires a special authentication method which is not supported by this module.
-3 - unknown username or invalid password - Self descriptive.
-
-class Socks5Error - This will be raised for Socks5 errors which are not related to
-authentication. The parameter is a tuple containing a code and a description of the
-error, as given by the server. The possible errors, according to the RFC are:
-
-1 - General SOCKS server failure - If for any reason the proxy server is unable to
-fulfill your request (internal server error).
-2 - connection not allowed by ruleset - If the address you're trying to connect to
-is blacklisted on the server or requires authentication.
-3 - Network unreachable - The target could not be contacted. A router on the network
-had replied with a destination net unreachable error.
-4 - Host unreachable - The target could not be contacted. A router on the network
-had replied with a destination host unreachable error.
-5 - Connection refused - The target server has actively refused the connection
-(the requested port is closed).
-6 - TTL expired - The TTL value of the SYN packet from the proxy to the target server
-has expired. This usually means that there are network problems causing the packet
-to be caught in a router-to-router "ping-pong".
-7 - Command not supported - The client has issued an invalid command. When using this
-module, this error should not occur.
-8 - Address type not supported - The client has provided an invalid address type.
-When using this module, this error should not occur.
-
-class Socks4Error - This will be raised for Socks4 errors. The parameter is a tuple
-containing a code and a description of the error, as given by the server. The
-possible error, according to the specification are:
-
-1 - Request rejected or failed - Will be raised in the event of an failure for any
-reason other then the two mentioned next.
-2 - request rejected because SOCKS server cannot connect to identd on the client -
-The Socks server had tried an ident lookup on your computer and has failed. In this
-case you should run an identd server and/or configure your firewall to allow incoming
-connections to local port 113 from the remote server.
-3 - request rejected because the client program and identd report different user-ids -
-The Socks server had performed an ident lookup on your computer and has received a
-different userid than the one you have provided. Change your userid (through the
-username parameter of the setproxy method) to match and try again.
-
-class HTTPError - This will be raised for HTTP errors. The parameter is a tuple
-containing the HTTP status code and the description of the server.
-
-
-After establishing the connection, the object behaves like a standard socket.
-Call the close method to close the connection.
-
-In addition to the socksocket class, an additional function worth mentioning is the
-setdefaultproxy function. The parameters are the same as the setproxy method.
-This function will set default proxy settings for newly created socksocket objects,
-in which the proxy settings haven't been changed via the setproxy method.
-This is quite useful if you wish to force 3rd party modules to use a socks proxy,
-by overriding the socket object.
-For example:
-
->>> socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5,"socks.example.com")
->>> socket.socket = socks.socksocket
->>> urllib.urlopen("http://www.sourceforge.net/")
-
-
-PROBLEMS
----------
-
-If you have any problems using this module, please first refer to the BUGS file
-(containing current bugs and issues). If your problem is not mentioned you may
-contact the author at the following E-Mail address:
-
-negativeiq@users.sourceforge.net
-
-Please allow some time for your question to be received and handled.
-
-
-Dan-Haim,
-Author.
diff --git a/src/socks/__init__.py b/src/socks/__init__.py
deleted file mode 100644
index 7fd2cba3..00000000
--- a/src/socks/__init__.py
+++ /dev/null
@@ -1,476 +0,0 @@
-"""SocksiPy - Python SOCKS module.
-Version 1.00
-
-Copyright 2006 Dan-Haim. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- 3. Neither the name of Dan Haim nor the names of his contributors may be used
- to endorse or promote products derived from this software without specific
- prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
-
-
-This module provides a standard socket-like interface for Python
-for tunneling connections through SOCKS proxies.
-
-"""
-
-"""
-
-Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
-for use in PyLoris (http://pyloris.sourceforge.net/)
-
-Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
-mainly to merge bug fixes found in Sourceforge
-
-"""
-
-import socket
-import struct
-import sys
-
-PROXY_TYPE_SOCKS4 = 1
-PROXY_TYPE_SOCKS5 = 2
-PROXY_TYPE_HTTP = 3
-
-_defaultproxy = None
-_orgsocket = socket.socket
-
-class ProxyError(Exception): pass
-class GeneralProxyError(ProxyError): pass
-class Socks5AuthError(ProxyError): pass
-class Socks5Error(ProxyError): pass
-class Socks4Error(ProxyError): pass
-class HTTPError(ProxyError): pass
-
-_generalerrors = ("success",
- "invalid data",
- "not connected",
- "not available",
- "bad proxy type",
- "bad input",
- "timed out",
- "network unreachable",
- "connection refused",
- "host unreachable")
-
-_socks5errors = ("succeeded",
- "general SOCKS server failure",
- "connection not allowed by ruleset",
- "Network unreachable",
- "Host unreachable",
- "Connection refused",
- "TTL expired",
- "Command not supported",
- "Address type not supported",
- "Unknown error")
-
-_socks5autherrors = ("succeeded",
- "authentication is required",
- "all offered authentication methods were rejected",
- "unknown username or invalid password",
- "unknown error")
-
-_socks4errors = ("request granted",
- "request rejected or failed",
- "request rejected because SOCKS server cannot connect to identd on the client",
- "request rejected because the client program and identd report different user-ids",
- "unknown error")
-
-def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
- """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
- Sets a default proxy which all further socksocket objects will use,
- unless explicitly changed.
- """
- global _defaultproxy
- _defaultproxy = (proxytype, addr, port, rdns, username, password)
-
-def wrapmodule(module):
- """wrapmodule(module)
- Attempts to replace a module's socket library with a SOCKS socket. Must set
- a default proxy using setdefaultproxy(...) first.
- This will only work on modules that import socket directly into the namespace;
- most of the Python Standard Library falls into this category.
- """
- if _defaultproxy != None:
- module.socket.socket = socksocket
- else:
- raise GeneralProxyError((4, "no proxy specified"))
-
-class socksocket(socket.socket):
- """socksocket([family[, type[, proto]]]) -> socket object
- Open a SOCKS enabled socket. The parameters are the same as
- those of the standard socket init. In order for SOCKS to work,
- you must specify family=AF_INET, type=SOCK_STREAM and proto=0.
- """
-
- def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None):
- _orgsocket.__init__(self, family, type, proto, _sock)
- if _defaultproxy != None:
- self.__proxy = _defaultproxy
- else:
- self.__proxy = (None, None, None, None, None, None)
- self.__proxysockname = None
- self.__proxypeername = None
-
- def __recvall(self, count):
- """__recvall(count) -> data
- Receive EXACTLY the number of bytes requested from the socket.
- Blocks until the required number of bytes have been received.
- """
- try:
- data = self.recv(count)
- except socket.timeout:
- raise GeneralProxyError((6, "timed out"))
- while len(data) < count:
- d = self.recv(count-len(data))
- if not d: raise GeneralProxyError((0, "connection closed unexpectedly"))
- data = data + d
- return data
-
- def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
- """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
- Sets the proxy to be used.
-
- proxytype
- The type of the proxy to be used. Three types
- are supported: PROXY_TYPE_SOCKS4 (including socks4a),
- PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
-
- addr
- The address of the server (IP or DNS).
-
- port
- The port of the server. Defaults to 1080 for SOCKS
- servers and 8080 for HTTP proxy servers.
-
- rdns
- Should DNS queries be preformed on the remote side
- (rather than the local side). The default is True.
- Note: This has no effect with SOCKS4 servers.
-
- username
- Username to authenticate with to the server.
- The default is no authentication.
-
- password
- Password to authenticate with to the server.
- Only relevant when username is also provided.
-
- """
- self.__proxy = (proxytype, addr, port, rdns, username, password)
-
- def __negotiatesocks5(self):
- """__negotiatesocks5(self,destaddr,destport)
- Negotiates a connection through a SOCKS5 server.
- """
- # First we'll send the authentication packages we support.
- if (self.__proxy[4]!=None) and (self.__proxy[5]!=None):
- # The username/password details were supplied to the
- # setproxy method so we support the USERNAME/PASSWORD
- # authentication (in addition to the standard none).
- self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02))
- else:
- # No username/password were entered, therefore we
- # only support connections with no authentication.
- self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00))
- # We'll receive the server's response to determine which
- # method was selected
- chosenauth = self.__recvall(2)
- if chosenauth[0:1] != chr(0x05).encode():
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- # Check the chosen authentication method
- if chosenauth[1:2] == chr(0x00).encode():
- # No authentication is required
- pass
- elif chosenauth[1:2] == chr(0x02).encode():
- # Okay, we need to perform a basic username/password
- # authentication.
- self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5])
- authstat = self.__recvall(2)
- if authstat[0:1] != chr(0x01).encode():
- # Bad response
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- if authstat[1:2] != chr(0x00).encode():
- # Authentication failed
- self.close()
- raise Socks5AuthError((3, _socks5autherrors[3]))
- # Authentication succeeded
- else:
- # Reaching here is always bad
- self.close()
- if chosenauth[1] == chr(0xFF).encode():
- raise Socks5AuthError((2, _socks5autherrors[2]))
- else:
- raise GeneralProxyError((1, _generalerrors[1]))
-
- def __connectsocks5(self, destaddr, destport):
- # Now we can request the actual connection
- req = struct.pack('BBB', 0x05, 0x01, 0x00)
- # If the given destination address is an IP address, we'll
- # use the IPv4 address request even if remote resolving was specified.
- try:
- ipaddr = socket.inet_aton(destaddr)
- req = req + chr(0x01).encode() + ipaddr
- except socket.error:
- # Well it's not an IP number, so it's probably a DNS name.
- if self.__proxy[3]:
- # Resolve remotely
- ipaddr = None
- req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr
- else:
- # Resolve locally
- ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
- req = req + chr(0x01).encode() + ipaddr
- req = req + struct.pack(">H", destport)
- self.sendall(req)
- # Get the response
- resp = self.__recvall(4)
- if resp[0:1] != chr(0x05).encode():
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- elif resp[1:2] != chr(0x00).encode():
- # Connection failed
- self.close()
- if ord(resp[1:2])<=8:
- raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
- else:
- raise Socks5Error((9, _socks5errors[9]))
- # Get the bound address/port
- elif resp[3:4] == chr(0x01).encode():
- boundaddr = self.__recvall(4)
- elif resp[3:4] == chr(0x03).encode():
- resp = resp + self.recv(1)
- boundaddr = self.__recvall(ord(resp[4:5]))
- else:
- self.close()
- raise GeneralProxyError((1,_generalerrors[1]))
- boundport = struct.unpack(">H", self.__recvall(2))[0]
- self.__proxysockname = (boundaddr, boundport)
- if ipaddr != None:
- self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
- else:
- self.__proxypeername = (destaddr, destport)
-
- def __resolvesocks5(self, host):
- # Now we can request the actual connection
- req = struct.pack('BBB', 0x05, 0xF0, 0x00)
- req += chr(0x03).encode() + chr(len(host)).encode() + host
- req = req + struct.pack(">H", 8444)
- self.sendall(req)
- # Get the response
- ip = ""
- resp = self.__recvall(4)
- if resp[0:1] != chr(0x05).encode():
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- elif resp[1:2] != chr(0x00).encode():
- # Connection failed
- self.close()
- if ord(resp[1:2])<=8:
- raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
- else:
- raise Socks5Error((9, _socks5errors[9]))
- # Get the bound address/port
- elif resp[3:4] == chr(0x01).encode():
- ip = socket.inet_ntoa(self.__recvall(4))
- elif resp[3:4] == chr(0x03).encode():
- resp = resp + self.recv(1)
- ip = self.__recvall(ord(resp[4:5]))
- else:
- self.close()
- raise GeneralProxyError((1,_generalerrors[1]))
- boundport = struct.unpack(">H", self.__recvall(2))[0]
- return ip
-
- def getproxysockname(self):
- """getsockname() -> address info
- Returns the bound IP address and port number at the proxy.
- """
- return self.__proxysockname
-
- def getproxypeername(self):
- """getproxypeername() -> address info
- Returns the IP and port number of the proxy.
- """
- return _orgsocket.getpeername(self)
-
- def getpeername(self):
- """getpeername() -> address info
- Returns the IP address and port number of the destination
- machine (note: getproxypeername returns the proxy)
- """
- return self.__proxypeername
-
- def getproxytype(self):
- return self.__proxy[0]
-
- def __negotiatesocks4(self,destaddr,destport):
- """__negotiatesocks4(self,destaddr,destport)
- Negotiates a connection through a SOCKS4 server.
- """
- # Check if the destination address provided is an IP address
- rmtrslv = False
- try:
- ipaddr = socket.inet_aton(destaddr)
- except socket.error:
- # It's a DNS name. Check where it should be resolved.
- if self.__proxy[3]:
- ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)
- rmtrslv = True
- else:
- ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
- # Construct the request packet
- req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
- # The username parameter is considered userid for SOCKS4
- if self.__proxy[4] != None:
- req = req + self.__proxy[4]
- req = req + chr(0x00).encode()
- # DNS name if remote resolving is required
- # NOTE: This is actually an extension to the SOCKS4 protocol
- # called SOCKS4A and may not be supported in all cases.
- if rmtrslv:
- req = req + destaddr + chr(0x00).encode()
- self.sendall(req)
- # Get the response from the server
- resp = self.__recvall(8)
- if resp[0:1] != chr(0x00).encode():
- # Bad data
- self.close()
- raise GeneralProxyError((1,_generalerrors[1]))
- if resp[1:2] != chr(0x5A).encode():
- # Server returned an error
- self.close()
- if ord(resp[1:2]) in (91, 92, 93):
- self.close()
- raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90]))
- else:
- raise Socks4Error((94, _socks4errors[4]))
- # Get the bound address/port
- self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0])
- if rmtrslv != None:
- self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
- else:
- self.__proxypeername = (destaddr, destport)
-
- def __negotiatehttp(self, destaddr, destport):
- """__negotiatehttp(self,destaddr,destport)
- Negotiates a connection through an HTTP server.
- """
- # If we need to resolve locally, we do this now
- if not self.__proxy[3]:
- addr = socket.gethostbyname(destaddr)
- else:
- addr = destaddr
- self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode())
- # We read the response until we get the string "\r\n\r\n"
- resp = self.recv(1)
- while resp.find("\r\n\r\n".encode()) == -1:
- resp = resp + self.recv(1)
- # We just need the first line to check if the connection
- # was successful
- statusline = resp.splitlines()[0].split(" ".encode(), 2)
- if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()):
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- try:
- statuscode = int(statusline[1])
- except ValueError:
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- if statuscode != 200:
- self.close()
- raise HTTPError((statuscode, statusline[2]))
- self.__proxysockname = ("0.0.0.0", 0)
- self.__proxypeername = (addr, destport)
-
- def connect(self, destpair):
- """connect(self, despair)
- Connects to the specified destination through a proxy.
- destpar - A tuple of the IP/DNS address and the port number.
- (identical to socket's connect).
- To select the proxy server use setproxy().
- """
- # Do a minimal input check first
- if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int):
- raise GeneralProxyError((5, _generalerrors[5]))
- if self.__proxy[0] == PROXY_TYPE_SOCKS5:
- if self.__proxy[2] != None:
- portnum = self.__proxy[2]
- else:
- portnum = 1080
- try:
- _orgsocket.connect(self, (self.__proxy[1], portnum))
- except socket.error as e:
- # ENETUNREACH, WSAENETUNREACH
- if e[0] in [101, 10051]:
- raise GeneralProxyError((7, _generalerrors[7]))
- # ECONNREFUSED, WSAECONNREFUSED
- if e[0] in [111, 10061]:
- raise GeneralProxyError((8, _generalerrors[8]))
- # EHOSTUNREACH, WSAEHOSTUNREACH
- if e[0] in [113, 10065]:
- raise GeneralProxyError((9, _generalerrors[9]))
- raise
- self.__negotiatesocks5()
- self.__connectsocks5(destpair[0], destpair[1])
- elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
- if self.__proxy[2] != None:
- portnum = self.__proxy[2]
- else:
- portnum = 1080
- _orgsocket.connect(self,(self.__proxy[1], portnum))
- self.__negotiatesocks4(destpair[0], destpair[1])
- elif self.__proxy[0] == PROXY_TYPE_HTTP:
- if self.__proxy[2] != None:
- portnum = self.__proxy[2]
- else:
- portnum = 8080
- try:
- _orgsocket.connect(self,(self.__proxy[1], portnum))
- except socket.error as e:
- # ENETUNREACH, WSAENETUNREACH
- if e[0] in [101, 10051]:
- raise GeneralProxyError((7, _generalerrors[7]))
- # ECONNREFUSED, WSAECONNREFUSED
- if e[0] in [111, 10061]:
- raise GeneralProxyError((8, _generalerrors[8]))
- # EHOSTUNREACH, WSAEHOSTUNREACH
- if e[0] in [113, 10065]:
- raise GeneralProxyError((9, _generalerrors[9]))
- raise
- self.__negotiatehttp(destpair[0], destpair[1])
- elif self.__proxy[0] == None:
- _orgsocket.connect(self, (destpair[0], destpair[1]))
- else:
- raise GeneralProxyError((4, _generalerrors[4]))
-
- def resolve(self, host):
- if self.__proxy[0] == PROXY_TYPE_SOCKS5:
- if self.__proxy[2] != None:
- portnum = self.__proxy[2]
- else:
- portnum = 1080
- _orgsocket.connect(self, (self.__proxy[1], portnum))
- self.__negotiatesocks5()
- return self.__resolvesocks5(host)
- else:
- return None
From a7cfe5ba326b950d882619f88ae17b3cd2d18ba3 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Fri, 9 Aug 2019 17:15:00 +0300
Subject: [PATCH 14/39] Try to test with tor
---
.travis.yml | 1 +
requirements.txt | 1 +
src/bitmessagemain.py | 5 +++--
src/plugins/proxyconfig_stem.py | 13 +++++++++++--
src/tests/core.py | 28 +++++++++++++++++++++-------
5 files changed, 37 insertions(+), 11 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 1edba418..d7141188 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,7 @@ addons:
packages:
- build-essential
- libcap-dev
+ - tor
install:
- pip install -r requirements.txt
- ln -s src pybitmessage # tests environment
diff --git a/requirements.txt b/requirements.txt
index c55e5cf1..be429a9f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
python_prctl
psutil
pycrypto
+stem
diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py
index 5dcfea6c..1dd2f271 100755
--- a/src/bitmessagemain.py
+++ b/src/bitmessagemain.py
@@ -193,7 +193,8 @@ class Main:
from plugins.plugin import get_plugin
try:
proxyconfig_start = time.time()
- get_plugin('proxyconfig', name=proxy_type)(config)
+ if not get_plugin('proxyconfig', name=proxy_type)(config):
+ raise TypeError
except TypeError:
logger.error(
'Failed to run proxy config plugin %s',
@@ -424,7 +425,7 @@ class Main:
self.stop()
elif not state.enableGUI:
from tests import core as test_core # pylint: disable=relative-import
- test_core_result = test_core.run()
+ test_core_result = test_core.run(self)
state.enableGUI = True
self.stop()
test_core.cleanup()
diff --git a/src/plugins/proxyconfig_stem.py b/src/plugins/proxyconfig_stem.py
index 75605c07..e8b5417e 100644
--- a/src/plugins/proxyconfig_stem.py
+++ b/src/plugins/proxyconfig_stem.py
@@ -8,6 +8,7 @@ import tempfile
import stem
import stem.control
import stem.process
+import stem.version
class DebugLogger(object):
@@ -31,7 +32,7 @@ class DebugLogger(object):
self._logger.log(self._levels.get(level, 10), '(tor)' + line)
-def connect_plugin(config):
+def connect_plugin(config): # pylint: disable=too-many-branches
"""Run stem proxy configurator"""
logwrite = DebugLogger()
if config.safeGet('bitmessagesettings', 'sockshostname') not in (
@@ -60,8 +61,14 @@ def connect_plugin(config):
# So if there is a system wide tor, use it for outbound connections.
try:
stem.process.launch_tor_with_config(
- tor_config, take_ownership=True, init_msg_handler=logwrite)
+ tor_config, take_ownership=True, timeout=20,
+ init_msg_handler=logwrite)
except OSError:
+ if not attempt:
+ try:
+ stem.version.get_system_tor_version()
+ except IOError:
+ return
continue
else:
logwrite('Started tor on port %s' % port)
@@ -108,3 +115,5 @@ def connect_plugin(config):
onionhostname, 'keytype', response.private_key_type)
config.save()
config.set('bitmessagesettings', 'socksproxytype', 'SOCKS5')
+
+ return True
diff --git a/src/tests/core.py b/src/tests/core.py
index a9df05fc..005900d0 100644
--- a/src/tests/core.py
+++ b/src/tests/core.py
@@ -21,6 +21,7 @@ from network.tcp import Socks4aBMConnection, Socks5BMConnection, TCPConnection
from queues import excQueue
knownnodes_file = os.path.join(state.appdata, 'knownnodes.dat')
+program = None
def pickle_knownnodes():
@@ -132,6 +133,7 @@ class TestCore(unittest.TestCase):
self._outdate_knownnodes()
# time.sleep(303) # singleCleaner wakes up every 5 min
knownnodes.cleanupKnownNodes()
+ self.assertTrue(knownnodes.knownNodes[1])
while True:
try:
thread, exc = excQueue.get(block=False)
@@ -140,14 +142,15 @@ class TestCore(unittest.TestCase):
if thread == 'Asyncore' and isinstance(exc, IndexError):
self.fail("IndexError because of empty knownNodes!")
- def test_bootstrap(self):
- """test bootstrapping"""
+ def _initiate_bootstrap(self):
BMConfigParser().set('bitmessagesettings', 'dontconnect', 'true')
self._outdate_knownnodes()
- knownnodes.cleanupKnownNodes()
- # it's weird, knownnodes appear empty
knownnodes.addKnownNode(1, state.Peer('127.0.0.1', 8444), is_self=True)
- time.sleep(0.25)
+ knownnodes.cleanupKnownNodes()
+ time.sleep(2)
+
+ def _check_bootstrap(self):
+ _started = time.time()
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
proxy_type = BMConfigParser().safeGet(
'bitmessagesettings', 'socksproxytype')
@@ -157,20 +160,31 @@ class TestCore(unittest.TestCase):
connection_base = Socks4aBMConnection
else:
connection_base = TCPConnection
- _started = time.time()
for _ in range(180):
time.sleep(1)
for peer, con in BMConnectionPool().outboundConnections.iteritems():
if not peer.host.startswith('bootstrap'):
self.assertIsInstance(con, connection_base)
+ self.assertNotEqual(peer.host, '127.0.0.1')
return
else: # pylint: disable=useless-else-on-loop
self.fail(
'Failed to connect during %s sec' % (time.time() - _started))
+ def test_bootstrap(self):
+ """test bootstrapping"""
+ self._initiate_bootstrap()
+ self._check_bootstrap()
+ self._initiate_bootstrap()
+ BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'stem')
+ program.start_proxyconfig(BMConfigParser())
+ self._check_bootstrap()
-def run():
+
+def run(prog):
"""Starts all tests defined in this module"""
+ global program # pylint: disable=global-statement
+ program = prog
loader = unittest.TestLoader()
loader.sortTestMethodsUsing = None
suite = loader.loadTestsFromTestCase(TestCore)
From 88f2c51595470bc83f1f10740b01a7eab4dc5e62 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Mon, 2 Sep 2019 14:19:45 +0300
Subject: [PATCH 15/39] quzwelsuziwqgpt2.onion:8444 is also a bootstrap server
---
src/knownnodes.py | 13 +++----------
src/network/connectionpool.py | 11 +++++++++--
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/knownnodes.py b/src/knownnodes.py
index 2735edbf..ba21bac7 100644
--- a/src/knownnodes.py
+++ b/src/knownnodes.py
@@ -34,10 +34,6 @@ DEFAULT_NODES = (
state.Peer('178.11.46.221', 8444)
)
-DEFAULT_NODES_ONION = (
- state.Peer('quzwelsuziwqgpt2.onion', 8444),
-)
-
def json_serialize_knownnodes(output):
"""
@@ -66,8 +62,7 @@ def json_deserialize_knownnodes(source):
if (
not (knownNodesActual or info.get('self')) and
- peer not in DEFAULT_NODES and
- peer not in DEFAULT_NODES_ONION
+ peer not in DEFAULT_NODES
):
knownNodesActual = True
@@ -102,9 +97,9 @@ def addKnownNode(stream, peer, lastseen=None, is_self=False):
}
-def createDefaultKnownNodes(onion=False):
+def createDefaultKnownNodes():
past = time.time() - 2418600 # 28 days - 10 min
- for peer in DEFAULT_NODES_ONION if onion else DEFAULT_NODES:
+ for peer in DEFAULT_NODES:
addKnownNode(1, peer, past)
saveKnownNodes()
@@ -122,8 +117,6 @@ def readKnownNodes():
logger.debug(
'Failed to read nodes from knownnodes.dat', exc_info=True)
createDefaultKnownNodes()
- if BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
- createDefaultKnownNodes(onion=True)
config = BMConfigParser()
diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py
index 1554a585..4d16df49 100644
--- a/src/network/connectionpool.py
+++ b/src/network/connectionpool.py
@@ -165,10 +165,14 @@ class BMConnectionPool(object):
proxy_type = BMConfigParser().safeGet(
'bitmessagesettings', 'socksproxytype')
# A plugins may be added here
+ hostname = None
if not proxy_type or proxy_type == 'none':
connection_base = TCPConnection
elif proxy_type == 'SOCKS5':
connection_base = Socks5BMConnection
+ hostname = helper_random.randomchoice([
+ 'quzwelsuziwqgpt2.onion', None
+ ])
elif proxy_type == 'SOCKS4a':
connection_base = Socks4aBMConnection # FIXME: I cannot test
else:
@@ -177,8 +181,11 @@ class BMConnectionPool(object):
return
bootstrapper = bootstrap(connection_base)
- port = helper_random.randomchoice([8080, 8444])
- hostname = 'bootstrap%s.bitmessage.org' % port
+ if not hostname:
+ port = helper_random.randomchoice([8080, 8444])
+ hostname = 'bootstrap%s.bitmessage.org' % port
+ else:
+ port = 8444
self.addConnection(bootstrapper(hostname, port))
def loop(self): # pylint: disable=too-many-branches,too-many-statements
From 8ed1d48799e8176c0dbe6b2f234dd4948f0d54db Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Thu, 26 Sep 2019 16:54:59 +0530
Subject: [PATCH 16/39] core pylint fixes
---
src/tests/core.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/tests/core.py b/src/tests/core.py
index 005900d0..b01c6b69 100644
--- a/src/tests/core.py
+++ b/src/tests/core.py
@@ -25,6 +25,7 @@ program = None
def pickle_knownnodes():
+ """Generate old style pickled knownnodes.dat"""
now = time.time()
with open(knownnodes_file, 'wb') as dst:
pickle.dump({
@@ -40,6 +41,7 @@ def pickle_knownnodes():
def cleanup():
+ """Cleanup application files"""
os.remove(knownnodes_file)
From 7839f83f20a2e887a9b68bd4da099ba966b02972 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Thu, 26 Sep 2019 16:56:55 +0530
Subject: [PATCH 17/39] test_api pylint fixes
---
src/tests/test_api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/tests/test_api.py b/src/tests/test_api.py
index dfe1b273..44505ffe 100644
--- a/src/tests/test_api.py
+++ b/src/tests/test_api.py
@@ -31,7 +31,7 @@ class TestAPIShutdown(TestAPIProto, TestProcessShutdown):
"""Separate test case for API command 'shutdown'"""
def test_shutdown(self):
"""Shutdown the pybitmessage"""
- self.assertEquals(self.api.shutdown(), 'done')
+ self.assertEqual(self.api.shutdown(), 'done')
for _ in range(5):
if not self.process.is_running():
break
From 944c30f9b41fc3b557821edc5a0990bb4e22f008 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Thu, 26 Sep 2019 16:57:17 +0530
Subject: [PATCH 18/39] test_config pylint fixes
---
src/tests/test_config.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/tests/test_config.py b/src/tests/test_config.py
index a0a727e5..35ddd3fa 100644
--- a/src/tests/test_config.py
+++ b/src/tests/test_config.py
@@ -26,6 +26,7 @@ class TestConfig(unittest.TestCase):
False
)
# no arg for default
+ # pylint: disable=too-many-function-args
with self.assertRaises(TypeError):
BMConfigParser().safeGetBoolean(
'nonexistent', 'nonexistent', True)
@@ -47,9 +48,9 @@ class TestProcessConfig(TestProcessProto):
config = BMConfigParser()
config.read(os.path.join(self.home, 'keys.dat'))
- self.assertEquals(config.safeGetInt(
+ self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'settingsversion'), 10)
- self.assertEquals(config.safeGetInt(
+ self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'port'), 8444)
# don't connect
self.assertTrue(config.safeGetBoolean(
@@ -59,7 +60,7 @@ class TestProcessConfig(TestProcessProto):
'bitmessagesettings', 'apienabled'))
# extralowdifficulty is false
- self.assertEquals(config.safeGetInt(
+ self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultnoncetrialsperbyte'), 1000)
- self.assertEquals(config.safeGetInt(
+ self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultpayloadlengthextrabytes'), 1000)
From fa65b17fc98a5689197beae21f3aa8be4be7f31f Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 14:51:54 +0530
Subject: [PATCH 19/39] __init__ pylint fixes
---
src/pyelliptic/__init__.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/pyelliptic/__init__.py b/src/pyelliptic/__init__.py
index 1d6a928f..7aa666e0 100644
--- a/src/pyelliptic/__init__.py
+++ b/src/pyelliptic/__init__.py
@@ -1,3 +1,7 @@
+"""
+src/pyelliptic/__init__.py
+=====================================
+"""
# Copyright (C) 2010
# Author: Yann GUIBET
# Contact:
From e0d81bb7e8e23c4d8612b0f8a382df3eddfd87f8 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 17:51:58 +0530
Subject: [PATCH 20/39] cipher pylint fixes
---
src/pyelliptic/cipher.py | 16 +++++++++++++---
src/pyelliptic/hash.py | 2 ++
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/src/pyelliptic/cipher.py b/src/pyelliptic/cipher.py
index bc1af6b0..d02b743a 100644
--- a/src/pyelliptic/cipher.py
+++ b/src/pyelliptic/cipher.py
@@ -1,5 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+"""
+src/pyelliptic/cipher.py
+========================
+"""
# Copyright (C) 2011 Yann GUIBET
# See LICENSE for details.
@@ -7,7 +11,8 @@
from openssl import OpenSSL
-class Cipher:
+# pylint: disable=redefined-builtin
+class Cipher(object):
"""
Symmetric encryption
@@ -44,30 +49,34 @@ class Cipher:
@staticmethod
def get_blocksize(ciphername):
+ """This Method returns cipher blocksize"""
cipher = OpenSSL.get_cipher(ciphername)
return cipher.get_blocksize()
@staticmethod
def gen_IV(ciphername):
+ """Generate random initialization vector"""
cipher = OpenSSL.get_cipher(ciphername)
return OpenSSL.rand(cipher.get_blocksize())
def update(self, input):
+ """Update result with more data"""
i = OpenSSL.c_int(0)
buffer = OpenSSL.malloc(b"", len(input) + self.cipher.get_blocksize())
inp = OpenSSL.malloc(input, len(input))
if OpenSSL.EVP_CipherUpdate(self.ctx, OpenSSL.byref(buffer),
OpenSSL.byref(i), inp, len(input)) == 0:
raise Exception("[OpenSSL] EVP_CipherUpdate FAIL ...")
- return buffer.raw[0:i.value]
+ return buffer.raw[0:i.value] # pylint: disable=invalid-slice-index
def final(self):
+ """Returning the final value"""
i = OpenSSL.c_int(0)
buffer = OpenSSL.malloc(b"", self.cipher.get_blocksize())
if (OpenSSL.EVP_CipherFinal_ex(self.ctx, OpenSSL.byref(buffer),
OpenSSL.byref(i))) == 0:
raise Exception("[OpenSSL] EVP_CipherFinal_ex FAIL ...")
- return buffer.raw[0:i.value]
+ return buffer.raw[0:i.value] # pylint: disable=invalid-slice-index
def ciphering(self, input):
"""
@@ -77,6 +86,7 @@ class Cipher:
return buff + self.final()
def __del__(self):
+ # pylint: disable=protected-access
if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL:
OpenSSL.EVP_CIPHER_CTX_reset(self.ctx)
else:
diff --git a/src/pyelliptic/hash.py b/src/pyelliptic/hash.py
index f2240500..2d7064f8 100644
--- a/src/pyelliptic/hash.py
+++ b/src/pyelliptic/hash.py
@@ -27,6 +27,7 @@ def _equals_str(a, b):
def equals(a, b):
+ """Compare two strings or bytearrays"""
if isinstance(a, str):
return _equals_str(a, b)
else:
@@ -58,6 +59,7 @@ def hmac_sha512(k, m):
def pbkdf2(password, salt=None, i=10000, keylen=64):
+ """Compute the salt, key and the message with pbkdf2"""
if salt is None:
salt = OpenSSL.rand(8)
p_password = OpenSSL.malloc(password, len(password))
From 4448e6ee7ba040c2582618a291e9cb28af57e511 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 17:52:56 +0530
Subject: [PATCH 21/39] hash pylint fixes
---
src/pyelliptic/hash.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/pyelliptic/hash.py b/src/pyelliptic/hash.py
index 2d7064f8..c21dd6a4 100644
--- a/src/pyelliptic/hash.py
+++ b/src/pyelliptic/hash.py
@@ -1,6 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-
+"""
+src/pyelliptic/hash.py
+=====================
+"""
# Copyright (C) 2011 Yann GUIBET
# See LICENSE for details.
@@ -30,8 +33,7 @@ def equals(a, b):
"""Compare two strings or bytearrays"""
if isinstance(a, str):
return _equals_str(a, b)
- else:
- return _equals_bytes(a, b)
+ return _equals_bytes(a, b)
def hmac_sha256(k, m):
@@ -59,7 +61,7 @@ def hmac_sha512(k, m):
def pbkdf2(password, salt=None, i=10000, keylen=64):
- """Compute the salt, key and the message with pbkdf2"""
+ """Key derivation function using SHA256"""
if salt is None:
salt = OpenSSL.rand(8)
p_password = OpenSSL.malloc(password, len(password))
From a6f951d37fe05a3b6518585cfdd6b11be6bc5f68 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Tue, 24 Sep 2019 13:05:42 +0530
Subject: [PATCH 22/39] openssl pylint fixes
---
src/pyelliptic/openssl.py | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/pyelliptic/openssl.py b/src/pyelliptic/openssl.py
index ab2990e6..fcde01ec 100644
--- a/src/pyelliptic/openssl.py
+++ b/src/pyelliptic/openssl.py
@@ -1,10 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-
+"""
+src/pyelliptic/openssl.py
+=====================
+"""
# Copyright (C) 2011 Yann GUIBET
# See LICENSE for details.
#
# Software slightly changed by Jonathan Warren
+# pylint: disable=protected-access
import sys
import ctypes
@@ -13,6 +17,9 @@ OpenSSL = None
class CipherName:
+ """Class returns cipher name, pointer and blocksize"""
+
+ # pylint: disable=old-style-class
def __init__(self, name, pointer, blocksize):
self._name = name
self._pointer = pointer
@@ -24,16 +31,20 @@ class CipherName:
" | Function pointer : " + str(self._pointer)
def get_pointer(self):
+ """This method returns cipher pointer"""
return self._pointer()
def get_name(self):
+ """This method returns cipher name"""
return self._name
def get_blocksize(self):
+ """This method returns cipher blocksize"""
return self._blocksize
def get_version(library):
+ """This function return version, hexversion and cflages"""
version = None
hexversion = None
cflags = None
@@ -68,6 +79,7 @@ class _OpenSSL:
"""
Wrapper for OpenSSL using ctypes
"""
+ # pylint: disable=too-many-statements, too-many-instance-attributes, old-style-class
def __init__(self, library):
"""
Build the wrapper
@@ -594,6 +606,7 @@ class _OpenSSL:
"""
returns the name of a elliptic curve with his id
"""
+ # pylint: disable=redefined-builtin
res = None
for i in self.curves:
if self.curves[i] == id:
@@ -607,6 +620,7 @@ class _OpenSSL:
"""
OpenSSL random function
"""
+ # pylint: disable=redefined-builtin
buffer = self.malloc(0, size)
# This pyelliptic library, by default, didn't check the return value of RAND_bytes. It is
# evidently possible that it returned an error and not-actually-random data. However, in
@@ -623,6 +637,7 @@ class _OpenSSL:
"""
returns a create_string_buffer (ctypes)
"""
+ # pylint: disable=redefined-builtin
buffer = None
if data != 0:
if sys.version_info.major == 3 and isinstance(data, type('')):
@@ -634,6 +649,8 @@ class _OpenSSL:
def loadOpenSSL():
+ """This function finds and load the OpenSSL library"""
+ # pylint: disable=global-statement
global OpenSSL
from os import path, environ
from ctypes.util import find_library
From 36775ae88b87cb6a126f9f0091b1a8dd41e8e0be Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 16:07:04 +0530
Subject: [PATCH 23/39] indicator_libmessaging pylint fixes
---
src/plugins/indicator_libmessaging.py | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/plugins/indicator_libmessaging.py b/src/plugins/indicator_libmessaging.py
index 36178663..ab2e833e 100644
--- a/src/plugins/indicator_libmessaging.py
+++ b/src/plugins/indicator_libmessaging.py
@@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
+"""
+src/plugins/indicator_libmessaging.py
+=====================================
+"""
import gi
gi.require_version('MessagingMenu', '1.0') # noqa:E402
@@ -9,6 +13,7 @@ from pybitmessage.tr import _translate
class IndicatorLibmessaging(object):
+ """Plugin for libmessage indicator"""
def __init__(self, form):
try:
self.app = MessagingMenu.App(desktop_id='pybitmessage.desktop')
@@ -32,15 +37,18 @@ class IndicatorLibmessaging(object):
if self.app:
self.app.unregister()
- def activate(self, app, source):
+ def activate(self, app, source): # pylint: disable=unused-argument
+ """Activate the libmessaging indicator plugin"""
self.form.appIndicatorInbox(
self.new_message_item if source == 'messages'
else self.new_broadcast_item
)
- # show the number of unread messages and subscriptions
- # on the messaging menu
def show_unread(self, draw_attention=False):
+ """
+ show the number of unread messages and subscriptions
+ on the messaging menu
+ """
for source, count in zip(
('messages', 'subscriptions'),
self.form.getUnread()
From 4c1568a3eb4c6748c2e26c56d50a60e1c17271b7 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 16:07:23 +0530
Subject: [PATCH 24/39] menu_qrcode pylint fixes
---
src/plugins/menu_qrcode.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/plugins/menu_qrcode.py b/src/plugins/menu_qrcode.py
index 7f21c6b8..2e65bd88 100644
--- a/src/plugins/menu_qrcode.py
+++ b/src/plugins/menu_qrcode.py
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
"""
+src/plugins/menu_qrcode.py
+==========================
+
A menu plugin showing QR-Code for bitmessage address in modal dialog.
"""
@@ -12,9 +15,10 @@ from pybitmessage.tr import _translate
# http://stackoverflow.com/questions/20452486
-class Image(qrcode.image.base.BaseImage):
+class Image(qrcode.image.base.BaseImage): # pylint: disable=abstract-method
"""Image output class for qrcode using QPainter"""
- def __init__(self, border, width, box_size):
+
+ def __init__(self, border, width, box_size): # pylint: disable=super-init-not-called
self.border = border
self.width = width
self.box_size = box_size
From 7aa9b94c111106ea739ea3890b351a5c953d6a43 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 16:08:00 +0530
Subject: [PATCH 25/39] notification_notify2 pylint fixes
---
src/plugins/notification_notify2.py | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/plugins/notification_notify2.py b/src/plugins/notification_notify2.py
index 3fd935c4..b4cd045d 100644
--- a/src/plugins/notification_notify2.py
+++ b/src/plugins/notification_notify2.py
@@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
+"""
+src/plugins/notification_notify2.py
+===================================
+"""
import gi
gi.require_version('Notify', '0.7')
@@ -6,10 +10,13 @@ from gi.repository import Notify
Notify.init('pybitmessage')
+
def connect_plugin(title, subtitle, category, label, icon):
+ """Plugin for notify2"""
if not icon:
icon = 'mail-message-new' if category == 2 else 'pybitmessage'
connect_plugin.notification.update(title, subtitle, icon)
connect_plugin.notification.show()
+
connect_plugin.notification = Notify.Notification.new("Init", "Init")
From e50f99419fba105a56b100c89958b281ff21cc14 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 16:08:18 +0530
Subject: [PATCH 26/39] plugin pylint fixes
---
src/plugins/plugin.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py
index 6601adaf..e671a73f 100644
--- a/src/plugins/plugin.py
+++ b/src/plugins/plugin.py
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
-
+"""
+src/plugins/plugin.py
+===================================
+"""
import pkg_resources
From df1994d6f3f02b833bbd690102ad8dfbbe3aa9c0 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 16:08:34 +0530
Subject: [PATCH 27/39] proxyconfig_stem pylint fixes
---
src/plugins/proxyconfig_stem.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/plugins/proxyconfig_stem.py b/src/plugins/proxyconfig_stem.py
index e8b5417e..5bb9a726 100644
--- a/src/plugins/proxyconfig_stem.py
+++ b/src/plugins/proxyconfig_stem.py
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
-
+"""
+src/plugins/proxyconfig_stem.py
+===================================
+"""
import os
import logging
import random # noseq
@@ -29,14 +32,14 @@ class DebugLogger(object):
# Plugin's debug or unexpected log line from tor
self._logger.debug(line)
else:
- self._logger.log(self._levels.get(level, 10), '(tor)' + line)
+ self._logger.log(self._levels.get(level, 10), '(tor) %s', line)
def connect_plugin(config): # pylint: disable=too-many-branches
"""Run stem proxy configurator"""
logwrite = DebugLogger()
if config.safeGet('bitmessagesettings', 'sockshostname') not in (
- 'localhost', '127.0.0.1', ''
+ 'localhost', '127.0.0.1', ''
):
# remote proxy is choosen for outbound connections,
# nothing to do here, but need to set socksproxytype to SOCKS5!
From a86c5188c44411d173bd2a15b24b7801b8bddf8b Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 16:08:56 +0530
Subject: [PATCH 28/39] sound_canberra pylint fixes
---
src/plugins/sound_canberra.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/plugins/sound_canberra.py b/src/plugins/sound_canberra.py
index 094901ed..dbb4baed 100644
--- a/src/plugins/sound_canberra.py
+++ b/src/plugins/sound_canberra.py
@@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
+"""
+src/plugins/proxyconfig_stem.py
+===================================
+"""
from pybitmessage.bitmessageqt import sound
@@ -14,7 +18,8 @@ _theme = {
}
-def connect_plugin(category, label=None):
+def connect_plugin(category, label=None): # pylint: disable=unused-argument
+ """This function implements the entry point."""
try:
_canberra.play(0, pycanberra.CA_PROP_EVENT_ID, _theme[category], None)
except (KeyError, pycanberra.CanberraException):
From a86e43c108c4117472868f2bcecdb0f4d20df31e Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 16:09:19 +0530
Subject: [PATCH 29/39] sound_gstreamer pylint fixes
---
src/plugins/sound_gstreamer.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/plugins/sound_gstreamer.py b/src/plugins/sound_gstreamer.py
index 062da3f9..32a0aa65 100644
--- a/src/plugins/sound_gstreamer.py
+++ b/src/plugins/sound_gstreamer.py
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
-
+"""
+src/plugins/sound_gstreamer.py
+===================================
+"""
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst # noqa: E402
@@ -9,6 +12,7 @@ _player = Gst.ElementFactory.make("playbin", "player")
def connect_plugin(sound_file):
+ """Entry point for sound file"""
_player.set_state(Gst.State.NULL)
_player.set_property("uri", "file://" + sound_file)
_player.set_state(Gst.State.PLAYING)
From 433cb9818b045843128a9be52f1bff2be6c06239 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 23 Sep 2019 16:09:35 +0530
Subject: [PATCH 30/39] sound_playfile pylint fixes
---
src/plugins/sound_playfile.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/plugins/sound_playfile.py b/src/plugins/sound_playfile.py
index c8216d07..6396c319 100644
--- a/src/plugins/sound_playfile.py
+++ b/src/plugins/sound_playfile.py
@@ -1,10 +1,14 @@
# -*- coding: utf-8 -*-
-
+"""
+src/plugins/sound_playfile.py
+===================================
+"""
try:
import winsound
def connect_plugin(sound_file):
+ """Plugin's entry point"""
winsound.PlaySound(sound_file, winsound.SND_FILENAME)
except ImportError:
import os
@@ -18,7 +22,8 @@ except ImportError:
args, stdout=FNULL, stderr=subprocess.STDOUT, close_fds=True)
def connect_plugin(sound_file):
- global play_cmd
+ """This function implements the entry point."""
+ global play_cmd # pylint: disable=global-statement
ext = os.path.splitext(sound_file)[-1]
try:
From 6f910f67c0ebb45f6b7455922f33c710a515ae7b Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Tue, 24 Sep 2019 14:50:20 +0530
Subject: [PATCH 31/39] filesystem flake8 fixes
---
src/storage/filesystem.py | 71 ++++++++++++++++++++++++++++++++-------
src/storage/sqlite.py | 14 ++++++--
src/storage/storage.py | 5 +++
3 files changed, 75 insertions(+), 15 deletions(-)
diff --git a/src/storage/filesystem.py b/src/storage/filesystem.py
index d64894a9..4f7e717d 100644
--- a/src/storage/filesystem.py
+++ b/src/storage/filesystem.py
@@ -3,11 +3,11 @@ from os import listdir, makedirs, path, remove, rmdir
import string
from threading import RLock
import time
-import traceback
from paths import lookupAppdataFolder
from storage import InventoryStorage, InventoryItem
+
class FilesystemInventory(InventoryStorage):
topDir = "inventory"
objectDir = "objects"
@@ -23,7 +23,9 @@ class FilesystemInventory(InventoryStorage):
raise IOError("%s exists but it's not a directory" % (createDir))
else:
makedirs(createDir)
- self.lock = RLock() # Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual)
+ # Guarantees that two receiveDataThreads don't receive and process the same message
+ # concurrently (probably sent by a malicious individual)
+ self.lock = RLock()
self._inventory = {}
self._load()
@@ -53,9 +55,25 @@ class FilesystemInventory(InventoryStorage):
except OSError:
pass
try:
- with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.metadataFilename), 'w') as f:
+ with open(
+ path.join(
+ self.baseDir,
+ FilesystemInventory.objectDir,
+ hexlify(hash),
+ FilesystemInventory.metadataFilename,
+ ),
+ "w",
+ ) as f:
f.write("%s,%s,%s,%s," % (value.type, value.stream, value.expires, hexlify(value.tag)))
- with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.dataFilename), 'w') as f:
+ with open(
+ path.join(
+ self.baseDir,
+ FilesystemInventory.objectDir,
+ hexlify(hash),
+ FilesystemInventory.dataFilename,
+ ),
+ "w",
+ ) as f:
f.write(value.payload)
except IOError:
raise KeyError
@@ -73,11 +91,21 @@ class FilesystemInventory(InventoryStorage):
pass
with self.lock:
try:
- remove(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.metadataFilename))
+ remove(
+ path.join(
+ self.baseDir,
+ FilesystemInventory.objectDir,
+ hexlify(hash),
+ FilesystemInventory.metadataFilename))
except IOError:
pass
try:
- remove(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.dataFilename))
+ remove(
+ path.join(
+ self.baseDir,
+ FilesystemInventory.objectDir,
+ hexlify(hash),
+ FilesystemInventory.dataFilename))
except IOError:
pass
try:
@@ -88,7 +116,7 @@ class FilesystemInventory(InventoryStorage):
def __iter__(self):
elems = []
for streamDict in self._inventory.values():
- elems.extend (streamDict.keys())
+ elems.extend(streamDict.keys())
return elems.__iter__()
def __len__(self):
@@ -103,10 +131,12 @@ class FilesystemInventory(InventoryStorage):
try:
objectType, streamNumber, expiresTime, tag = self.getMetadata(hashId)
try:
- newInventory[streamNumber][hashId] = InventoryItem(objectType, streamNumber, None, expiresTime, tag)
+ newInventory[streamNumber][hashId] = InventoryItem(
+ objectType, streamNumber, None, expiresTime, tag)
except KeyError:
newInventory[streamNumber] = {}
- newInventory[streamNumber][hashId] = InventoryItem(objectType, streamNumber, None, expiresTime, tag)
+ newInventory[streamNumber][hashId] = InventoryItem(
+ objectType, streamNumber, None, expiresTime, tag)
except KeyError:
print "error loading %s" % (hexlify(hashId))
pass
@@ -122,25 +152,42 @@ class FilesystemInventory(InventoryStorage):
def getData(self, hashId):
try:
- with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashId), FilesystemInventory.dataFilename), 'r') as f:
+ with open(
+ path.join(
+ self.baseDir,
+ FilesystemInventory.objectDir,
+ hexlify(hashId),
+ FilesystemInventory.dataFilename,
+ ),
+ "r",
+ ) as f:
return f.read()
except IOError:
raise AttributeError
def getMetadata(self, hashId):
try:
- with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashId), FilesystemInventory.metadataFilename), 'r') as f:
+ with open(
+ path.join(
+ self.baseDir,
+ FilesystemInventory.objectDir,
+ hexlify(hashId),
+ FilesystemInventory.metadataFilename,
+ ),
+ "r",
+ ) as f:
objectType, streamNumber, expiresTime, tag, undef = string.split(f.read(), ",", 4)
return [int(objectType), int(streamNumber), int(expiresTime), unhexlify(tag)]
except IOError:
raise KeyError
def by_type_and_tag(self, objectType, tag):
+ """Get a list of objects filtered by object type and tag"""
retval = []
for stream, streamDict in self._inventory:
for hashId, item in streamDict:
if item.type == objectType and item.tag == tag:
- try:
+ try:
if item.payload is None:
item.payload = self.getData(hashId)
except IOError:
diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py
index 438cbdcb..d1ec220f 100644
--- a/src/storage/sqlite.py
+++ b/src/storage/sqlite.py
@@ -10,9 +10,17 @@ from storage import InventoryStorage, InventoryItem
class SqliteInventory(InventoryStorage):
def __init__(self):
super(self.__class__, self).__init__()
- self._inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet).
- self._objects = {} # cache for existing objects, used for quick lookups if we have an object. This is used for example whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it.
- self.lock = RLock() # Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual)
+ # of objects (like msg payloads and pubkey payloads)
+ # Does not include protocol headers (the first 24 bytes of each packet).
+ self._inventory = {}
+ # cache for existing objects, used for quick lookups if we have an object.
+ # This is used for example whenever we receive an inv message from a peer
+ # to check to see what items are new to us.
+ # We don't delete things out of it; instead, the singleCleaner thread clears and refills it.
+ self._objects = {}
+ # Guarantees that two receiveDataThreads don't receive and process the same message concurrently
+ # (probably sent by a malicious individual)
+ self.lock = RLock()
def __contains__(self, hash):
with self.lock:
diff --git a/src/storage/storage.py b/src/storage/storage.py
index 08c85708..050197b8 100644
--- a/src/storage/storage.py
+++ b/src/storage/storage.py
@@ -3,6 +3,7 @@ import collections
InventoryItem = collections.namedtuple('InventoryItem', 'type stream payload expires tag')
class Storage(object):
+ """Base class for storing inventory (extendable for other items to store)"""
pass
# def __init__(self):
# super(self.__class__, self).__init__()
@@ -31,15 +32,19 @@ class InventoryStorage(Storage, collections.MutableMapping):
raise NotImplementedError
def by_type_and_tag(self, objectType, tag):
+ """Return objects filtered by object type and tag"""
raise NotImplementedError
def unexpired_hashes_by_stream(self, stream):
+ """Return unexpired inventory vectors filtered by stream"""
raise NotImplementedError
def flush(self):
+ """Flush cache"""
raise NotImplementedError
def clean(self):
+ """Free memory / perform garbage collection"""
raise NotImplementedError
class MailboxStorage(Storage, collections.MutableMapping):
From ac341482d4435869d4d9988da61d1450a15f8d4c Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Tue, 24 Sep 2019 14:51:53 +0530
Subject: [PATCH 32/39] filesystem pylint fixes
---
src/storage/filesystem.py | 55 ++++++++++++++++++++++++---------------
1 file changed, 34 insertions(+), 21 deletions(-)
diff --git a/src/storage/filesystem.py b/src/storage/filesystem.py
index 4f7e717d..43ba03fc 100644
--- a/src/storage/filesystem.py
+++ b/src/storage/filesystem.py
@@ -1,3 +1,7 @@
+"""
+src/storage/filesystem.py
+=========================
+"""
from binascii import hexlify, unhexlify
from os import listdir, makedirs, path, remove, rmdir
import string
@@ -8,14 +12,15 @@ from paths import lookupAppdataFolder
from storage import InventoryStorage, InventoryItem
-class FilesystemInventory(InventoryStorage):
+class FilesystemInventory(InventoryStorage): # pylint: disable=too-many-ancestors, abstract-method
+ """Module for using filesystem (directory with files) for inventory storage"""
topDir = "inventory"
objectDir = "objects"
metadataFilename = "metadata"
dataFilename = "data"
def __init__(self):
- super(self.__class__, self).__init__()
+ super(FilesystemInventory, self).__init__()
self.baseDir = path.join(lookupAppdataFolder(), FilesystemInventory.topDir)
for createDir in [self.baseDir, path.join(self.baseDir, "objects")]:
if path.exists(createDir):
@@ -29,29 +34,29 @@ class FilesystemInventory(InventoryStorage):
self._inventory = {}
self._load()
- def __contains__(self, hash):
+ def __contains__(self, hashval):
retval = False
for streamDict in self._inventory.values():
- if hash in streamDict:
+ if hashval in streamDict:
return True
return False
- def __getitem__(self, hash):
+ def __getitem__(self, hashval):
for streamDict in self._inventory.values():
try:
- retval = streamDict[hash]
+ retval = streamDict[hashval]
except KeyError:
continue
if retval.payload is None:
- retval = InventoryItem(retval.type, retval.stream, self.getData(hash), retval.expires, retval.tag)
+ retval = InventoryItem(retval.type, retval.stream, self.getData(hashval), retval.expires, retval.tag)
return retval
- raise KeyError(hash)
+ raise KeyError(hashval)
- def __setitem__(self, hash, value):
+ def __setitem__(self, hashval, value):
with self.lock:
value = InventoryItem(*value)
try:
- makedirs(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash)))
+ makedirs(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashval)))
except OSError:
pass
try:
@@ -59,7 +64,7 @@ class FilesystemInventory(InventoryStorage):
path.join(
self.baseDir,
FilesystemInventory.objectDir,
- hexlify(hash),
+ hexlify(hashval),
FilesystemInventory.metadataFilename,
),
"w",
@@ -69,7 +74,7 @@ class FilesystemInventory(InventoryStorage):
path.join(
self.baseDir,
FilesystemInventory.objectDir,
- hexlify(hash),
+ hexlify(hashval),
FilesystemInventory.dataFilename,
),
"w",
@@ -78,15 +83,16 @@ class FilesystemInventory(InventoryStorage):
except IOError:
raise KeyError
try:
- self._inventory[value.stream][hash] = value
+ self._inventory[value.stream][hashval] = value
except KeyError:
self._inventory[value.stream] = {}
- self._inventory[value.stream][hash] = value
+ self._inventory[value.stream][hashval] = value
- def delHashId(self, hash):
- for stream in self._inventory.keys():
+ def delHashId(self, hashval):
+ """Remove object from inventory"""
+ for stream in self._inventory:
try:
- del self._inventory[stream][hash]
+ del self._inventory[stream][hashval]
except KeyError:
pass
with self.lock:
@@ -95,7 +101,7 @@ class FilesystemInventory(InventoryStorage):
path.join(
self.baseDir,
FilesystemInventory.objectDir,
- hexlify(hash),
+ hexlify(hashval),
FilesystemInventory.metadataFilename))
except IOError:
pass
@@ -104,12 +110,12 @@ class FilesystemInventory(InventoryStorage):
path.join(
self.baseDir,
FilesystemInventory.objectDir,
- hexlify(hash),
+ hexlify(hashval),
FilesystemInventory.dataFilename))
except IOError:
pass
try:
- rmdir(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash)))
+ rmdir(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashval)))
except IOError:
pass
@@ -139,18 +145,20 @@ class FilesystemInventory(InventoryStorage):
objectType, streamNumber, None, expiresTime, tag)
except KeyError:
print "error loading %s" % (hexlify(hashId))
- pass
self._inventory = newInventory
# for i, v in self._inventory.items():
# print "loaded stream: %s, %i items" % (i, len(v))
def stream_list(self):
+ """Return list of streams"""
return self._inventory.keys()
def object_list(self):
+ """Return inventory vectors (hashes) from a directory"""
return [unhexlify(x) for x in listdir(path.join(self.baseDir, FilesystemInventory.objectDir))]
def getData(self, hashId):
+ """Get object data"""
try:
with open(
path.join(
@@ -166,6 +174,7 @@ class FilesystemInventory(InventoryStorage):
raise AttributeError
def getMetadata(self, hashId):
+ """Get object metadata"""
try:
with open(
path.join(
@@ -196,12 +205,14 @@ class FilesystemInventory(InventoryStorage):
return retval
def hashes_by_stream(self, stream):
+ """Return inventory vectors (hashes) for a stream"""
try:
return self._inventory[stream].keys()
except KeyError:
return []
def unexpired_hashes_by_stream(self, stream):
+ """Return unexpired hashes in the inventory for a particular stream"""
t = int(time.time())
try:
return [x for x, value in self._inventory[stream].items() if value.expires > t]
@@ -209,9 +220,11 @@ class FilesystemInventory(InventoryStorage):
return []
def flush(self):
+ """Flush the inventory and create a new, empty one"""
self._load()
def clean(self):
+ """Clean out old items from the inventory"""
minTime = int(time.time()) - (60 * 60 * 30)
deletes = []
for stream, streamDict in self._inventory.items():
From 54ebbcb7db1f9ec0b2a0d0fe04ec189b70dd2b2a Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Wed, 25 Sep 2019 13:07:29 +0530
Subject: [PATCH 33/39] sqlite flake8 fixes
---
src/storage/sqlite.py | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py
index d1ec220f..eb41b5c0 100644
--- a/src/storage/sqlite.py
+++ b/src/storage/sqlite.py
@@ -1,12 +1,11 @@
-import collections
-from threading import current_thread, enumerate as threadingEnumerate, RLock
-import Queue
+from threading import RLock
import sqlite3
import time
-from helper_sql import *
+from helper_sql import sqlQuery, SqlBulkExecute, sqlExecute
from storage import InventoryStorage, InventoryItem
+
class SqliteInventory(InventoryStorage):
def __init__(self):
super(self.__class__, self).__init__()
@@ -36,7 +35,9 @@ class SqliteInventory(InventoryStorage):
with self.lock:
if hash in self._inventory:
return self._inventory[hash]
- rows = sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', sqlite3.Binary(hash))
+ rows = sqlQuery(
+ 'SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?',
+ sqlite3.Binary(hash))
if not rows:
raise KeyError(hash)
return InventoryItem(*rows[0])
@@ -63,18 +64,22 @@ class SqliteInventory(InventoryStorage):
def by_type_and_tag(self, objectType, tag):
with self.lock:
values = [value for value in self._inventory.values() if value.type == objectType and value.tag == tag]
- values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', objectType, sqlite3.Binary(tag)))
+ values += (InventoryItem(*value) for value in sqlQuery(
+ 'SELECT objecttype, streamnumber, payload, expirestime, tag \
+ FROM inventory WHERE objecttype=? AND tag=?', objectType, sqlite3.Binary(tag)))
return values
def unexpired_hashes_by_stream(self, stream):
with self.lock:
t = int(time.time())
hashes = [x for x, value in self._inventory.items() if value.stream == stream and value.expires > t]
- hashes += (str(payload) for payload, in sqlQuery('SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t))
+ hashes += (str(payload) for payload, in sqlQuery(
+ 'SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t))
return hashes
def flush(self):
- with self.lock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock.
+ with self.lock:
+ # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock.
with SqlBulkExecute() as sql:
for objectHash, value in self._inventory.items():
sql.execute('INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)', sqlite3.Binary(objectHash), *value)
@@ -82,8 +87,7 @@ class SqliteInventory(InventoryStorage):
def clean(self):
with self.lock:
- sqlExecute('DELETE FROM inventory WHERE expirestime',int(time.time()) - (60 * 60 * 3))
+ sqlExecute('DELETE FROM inventory WHERE expirestime', int(time.time()) - (60 * 60 * 3))
self._objects.clear()
for objectHash, value in self._inventory.items():
self._objects[objectHash] = value.stream
-
From da5d085a3903f5de733448df8894a2eda6e7ac3c Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Wed, 25 Sep 2019 15:16:28 +0530
Subject: [PATCH 34/39] sqlite pylint fixes
---
src/storage/sqlite.py | 37 +++++++++++++++++++++----------------
1 file changed, 21 insertions(+), 16 deletions(-)
diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py
index eb41b5c0..0c2b4afa 100644
--- a/src/storage/sqlite.py
+++ b/src/storage/sqlite.py
@@ -1,14 +1,19 @@
-from threading import RLock
+"""
+src/storage/sqlite.py
+=========================
+"""
import sqlite3
import time
+from threading import RLock
from helper_sql import sqlQuery, SqlBulkExecute, sqlExecute
from storage import InventoryStorage, InventoryItem
-class SqliteInventory(InventoryStorage):
+class SqliteInventory(InventoryStorage): # pylint: disable=too-many-ancestors
+ """Inventory using SQLite"""
def __init__(self):
- super(self.__class__, self).__init__()
+ super(SqliteInventory, self).__init__()
# of objects (like msg payloads and pubkey payloads)
# Does not include protocol headers (the first 24 bytes of each packet).
self._inventory = {}
@@ -21,34 +26,34 @@ class SqliteInventory(InventoryStorage):
# (probably sent by a malicious individual)
self.lock = RLock()
- def __contains__(self, hash):
+ def __contains__(self, hash_):
with self.lock:
- if hash in self._objects:
+ if hash_ in self._objects:
return True
- rows = sqlQuery('SELECT streamnumber FROM inventory WHERE hash=?', sqlite3.Binary(hash))
+ rows = sqlQuery('SELECT streamnumber FROM inventory WHERE hash=?', sqlite3.Binary(hash_))
if not rows:
return False
- self._objects[hash] = rows[0][0]
+ self._objects[hash_] = rows[0][0]
return True
- def __getitem__(self, hash):
+ def __getitem__(self, hash_):
with self.lock:
- if hash in self._inventory:
- return self._inventory[hash]
+ if hash_ in self._inventory:
+ return self._inventory[hash_]
rows = sqlQuery(
'SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?',
- sqlite3.Binary(hash))
+ sqlite3.Binary(hash_))
if not rows:
- raise KeyError(hash)
+ raise KeyError(hash_)
return InventoryItem(*rows[0])
- def __setitem__(self, hash, value):
+ def __setitem__(self, hash_, value):
with self.lock:
value = InventoryItem(*value)
- self._inventory[hash] = value
- self._objects[hash] = value.stream
+ self._inventory[hash_] = value
+ self._objects[hash_] = value.stream
- def __delitem__(self, hash):
+ def __delitem__(self, hash_):
raise NotImplementedError
def __iter__(self):
From e924e9208f3669d8ccfd8aaffb82dbbb6420b063 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Wed, 25 Sep 2019 16:39:15 +0530
Subject: [PATCH 35/39] storage flake8 fixes
---
src/storage/storage.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/storage/storage.py b/src/storage/storage.py
index 050197b8..4e603021 100644
--- a/src/storage/storage.py
+++ b/src/storage/storage.py
@@ -2,15 +2,17 @@ import collections
InventoryItem = collections.namedtuple('InventoryItem', 'type stream payload expires tag')
+
class Storage(object):
"""Base class for storing inventory (extendable for other items to store)"""
pass
# def __init__(self):
# super(self.__class__, self).__init__()
+
class InventoryStorage(Storage, collections.MutableMapping):
def __init__(self):
-# super(self.__class__, self).__init__()
+ # super(self.__class__, self).__init__()
self.numberOfInventoryLookupsPerformed = 0
def __contains__(self, hash):
@@ -47,7 +49,8 @@ class InventoryStorage(Storage, collections.MutableMapping):
"""Free memory / perform garbage collection"""
raise NotImplementedError
+
class MailboxStorage(Storage, collections.MutableMapping):
def __init__(self):
-# super(self.__class__, self).__init__()
+ # super(self.__class__, self).__init__()
pass
From fba2d6d8375fa6968dd1a0c01354e2f7b08ce490 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Thu, 26 Sep 2019 14:12:15 +0530
Subject: [PATCH 36/39] storage pylint fixes
---
src/storage/storage.py | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/src/storage/storage.py b/src/storage/storage.py
index 4e603021..482937e7 100644
--- a/src/storage/storage.py
+++ b/src/storage/storage.py
@@ -1,3 +1,7 @@
+"""
+src/storage/storage.py
+======================
+"""
import collections
InventoryItem = collections.namedtuple('InventoryItem', 'type stream payload expires tag')
@@ -6,25 +10,24 @@ InventoryItem = collections.namedtuple('InventoryItem', 'type stream payload exp
class Storage(object):
"""Base class for storing inventory (extendable for other items to store)"""
pass
-# def __init__(self):
-# super(self.__class__, self).__init__()
class InventoryStorage(Storage, collections.MutableMapping):
+ """Module used for inventory storage"""
def __init__(self):
- # super(self.__class__, self).__init__()
+ # pylint: disable=super-init-not-called
self.numberOfInventoryLookupsPerformed = 0
- def __contains__(self, hash):
+ def __contains__(self, _):
raise NotImplementedError
- def __getitem__(self, hash):
+ def __getitem__(self, _):
raise NotImplementedError
- def __setitem__(self, hash, value):
+ def __setitem__(self, _, value):
raise NotImplementedError
- def __delitem__(self, hash):
+ def __delitem__(self, _):
raise NotImplementedError
def __iter__(self):
@@ -50,7 +53,8 @@ class InventoryStorage(Storage, collections.MutableMapping):
raise NotImplementedError
-class MailboxStorage(Storage, collections.MutableMapping):
+class MailboxStorage(Storage, collections.MutableMapping): # pylint: disable=abstract-method
+ """Method for storing mails"""
def __init__(self):
- # super(self.__class__, self).__init__()
+ # pylint: disable=super-init-not-called
pass
From 40e15579fdeee55352a28545f664fc7bc8b602c6 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Fri, 27 Sep 2019 19:31:55 +0530
Subject: [PATCH 37/39] messagetypes init flake and pylint fixes
---
src/messagetypes/__init__.py | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/messagetypes/__init__.py b/src/messagetypes/__init__.py
index 06783eac..7319dfd5 100644
--- a/src/messagetypes/__init__.py
+++ b/src/messagetypes/__init__.py
@@ -1,3 +1,7 @@
+"""
+src/messagetypes/__init__.py
+============================
+"""
from importlib import import_module
from os import path, listdir
from string import lower
@@ -6,12 +10,15 @@ from debug import logger
import messagetypes
import paths
-class MsgBase(object):
- def encode(self):
+
+class MsgBase(object): # pylint: disable=too-few-public-methods
+ """Base class for message types"""
+ def __init__(self):
self.data = {"": lower(type(self).__name__)}
def constructObject(data):
+ """Constructing an object"""
whitelist = ["message"]
if data[""] not in whitelist:
return None
@@ -32,6 +39,7 @@ def constructObject(data):
else:
return returnObj
+
if paths.frozen is not None:
import messagetypes.message
import messagetypes.vote
From 9aa7dd9d78d4eda83f78f577af7b47ccd8694fa2 Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 30 Sep 2019 18:42:36 +0530
Subject: [PATCH 38/39] message pylint fixes
---
src/messagetypes/message.py | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/messagetypes/message.py b/src/messagetypes/message.py
index f52c6b35..cd5bf762 100644
--- a/src/messagetypes/message.py
+++ b/src/messagetypes/message.py
@@ -1,24 +1,30 @@
+"""
+src/messagetypes/message.py
+===========================
+"""
from debug import logger
from messagetypes import MsgBase
class Message(MsgBase):
- def __init__(self):
- return
+ """Encapsulate a message"""
+ # pylint: disable=attribute-defined-outside-init
def decode(self, data):
+ """Decode a message"""
# UTF-8 and variable type validator
- if type(data["subject"]) is str:
+ if isinstance(data["subject"], str):
self.subject = unicode(data["subject"], 'utf-8', 'replace')
else:
self.subject = unicode(str(data["subject"]), 'utf-8', 'replace')
- if type(data["body"]) is str:
+ if isinstance(data["body"], str):
self.body = unicode(data["body"], 'utf-8', 'replace')
else:
self.body = unicode(str(data["body"]), 'utf-8', 'replace')
def encode(self, data):
- super(Message, self).encode()
+ """Encode a message"""
+ super(Message, self).__init__()
try:
self.data["subject"] = data["subject"]
self.data["body"] = data["body"]
@@ -27,5 +33,6 @@ class Message(MsgBase):
return self.data
def process(self):
+ """Process a message"""
logger.debug("Subject: %i bytes", len(self.subject))
logger.debug("Body: %i bytes", len(self.body))
From e5b92e29a2d62a5549bd36786db2fa7c7af4eb2e Mon Sep 17 00:00:00 2001
From: lakshyacis
Date: Mon, 30 Sep 2019 18:42:50 +0530
Subject: [PATCH 39/39] vote pylint fixes
---
src/messagetypes/vote.py | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/messagetypes/vote.py b/src/messagetypes/vote.py
index df8d267f..e128e9ba 100644
--- a/src/messagetypes/vote.py
+++ b/src/messagetypes/vote.py
@@ -1,23 +1,31 @@
+"""
+src/messagetypes/vote.py
+========================
+"""
from debug import logger
from messagetypes import MsgBase
+
class Vote(MsgBase):
- def __init__(self):
- return
+ """Module used to vote"""
def decode(self, data):
+ """decode a vote"""
+ # pylint: disable=attribute-defined-outside-init
self.msgid = data["msgid"]
self.vote = data["vote"]
def encode(self, data):
- super(Vote, self).encode()
+ """Encode a vote"""
+ super(Vote, self).__init__()
try:
self.data["msgid"] = data["msgid"]
self.data["vote"] = data["vote"]
except KeyError as e:
- logger.error("Missing key %s", e.name)
+ logger.error("Missing key %s", e)
return self.data
def process(self):
+ """Encode a vote"""
logger.debug("msgid: %s", self.msgid)
logger.debug("vote: %s", self.vote)