Merge branch 'v0.6' into v0.6
This commit is contained in:
commit
e0efb7fd2f
49
Dockerfile
Normal file
49
Dockerfile
Normal file
|
@ -0,0 +1,49 @@
|
|||
# A container for PyBitmessage daemon
|
||||
|
||||
FROM ubuntu:xenial
|
||||
|
||||
RUN apt-get update
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get install -yq --no-install-suggests --no-install-recommends \
|
||||
python-msgpack dh-python python-all-dev build-essential libssl-dev \
|
||||
python-stdeb fakeroot python-pip libcap-dev
|
||||
|
||||
RUN pip install --upgrade pip
|
||||
|
||||
EXPOSE 8444 8442
|
||||
|
||||
ENV HOME /home/bitmessage
|
||||
ENV BITMESSAGE_HOME ${HOME}
|
||||
|
||||
ENV VER 0.6.3.2
|
||||
|
||||
WORKDIR ${HOME}
|
||||
ADD . ${HOME}
|
||||
|
||||
# Install tests dependencies
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# Build and install deb
|
||||
RUN python2 setup.py sdist \
|
||||
&& py2dsc-deb dist/pybitmessage-${VER}.tar.gz \
|
||||
&& dpkg -i deb_dist/python-pybitmessage_${VER}-1_amd64.deb
|
||||
|
||||
# Create a user
|
||||
RUN useradd bitmessage && chown -R bitmessage ${HOME}
|
||||
|
||||
USER bitmessage
|
||||
|
||||
# Generate default config
|
||||
RUN src/bitmessagemain.py -t && mv keys.dat /tmp
|
||||
|
||||
# Clean HOME
|
||||
RUN rm -rf ${HOME}/*
|
||||
|
||||
# Setup environment
|
||||
RUN mv /tmp/keys.dat . \
|
||||
&& APIPASS=$(tr -dc a-zA-Z0-9 < /dev/urandom | head -c32 && echo) \
|
||||
&& echo "\napiusername: api\napipassword: $APIPASS" \
|
||||
&& echo "apienabled = true\napiinterface = 0.0.0.0\napiusername = api\napipassword = $APIPASS" >> keys.dat
|
||||
|
||||
CMD ["pybitmessage", "-d"]
|
|
@ -1,3 +1,4 @@
|
|||
include COPYING
|
||||
include README.md
|
||||
include requirements.txt
|
||||
recursive-include desktop *
|
||||
|
|
|
@ -5,23 +5,11 @@ src/addresses.py
|
|||
"""
|
||||
# pylint: disable=redefined-outer-name,inconsistent-return-statements
|
||||
|
||||
from __future__ import print_function
|
||||
import hashlib
|
||||
from binascii import hexlify, unhexlify
|
||||
from struct import pack, unpack
|
||||
|
||||
from debug import logger
|
||||
from pyelliptic import arithmetic
|
||||
|
||||
|
||||
def convertIntToString(n):
|
||||
""".. todo:: There is another copy of this function in Bitmessagemain.py"""
|
||||
a = __builtins__.hex(n)
|
||||
if a[-1:] == 'L':
|
||||
a = a[:-1]
|
||||
if len(a) % 2 == 0:
|
||||
return unhexlify(a[2:])
|
||||
return unhexlify('0' + a[2:])
|
||||
|
||||
|
||||
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
|
@ -288,7 +276,10 @@ def addBMIfNotPresent(address):
|
|||
return address if address[:3] == 'BM-' else 'BM-' + address
|
||||
|
||||
|
||||
# TODO: make test case
|
||||
if __name__ == "__main__":
|
||||
from pyelliptic import arithmetic
|
||||
|
||||
print(
|
||||
'\nLet us make an address from scratch. Suppose we generate two'
|
||||
' random 32 byte values and call the first one the signing key'
|
||||
|
|
|
@ -661,8 +661,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|||
BMConfigParser().remove_section(address)
|
||||
with open(state.appdata + 'keys.dat', 'wb') as configfile:
|
||||
BMConfigParser().write(configfile)
|
||||
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
|
||||
queues.UISignalQueue.put(('rerenderMessagelistToLabels', ''))
|
||||
queues.UISignalQueue.put(('writeNewAddressToTable', ('', '', '')))
|
||||
shared.reloadMyAddressHashes()
|
||||
return 'success'
|
||||
|
||||
|
|
|
@ -22,10 +22,13 @@ depends.check_dependencies()
|
|||
|
||||
import ctypes
|
||||
import getopt
|
||||
import multiprocessing
|
||||
# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
|
||||
import signal
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
from struct import pack
|
||||
|
||||
from helper_startup import (
|
||||
|
@ -38,7 +41,7 @@ import shared
|
|||
import knownnodes
|
||||
import state
|
||||
import shutdown
|
||||
import threading
|
||||
from debug import logger
|
||||
|
||||
# Classes
|
||||
from class_sqlThread import sqlThread
|
||||
|
@ -61,9 +64,9 @@ from network.downloadthread import DownloadThread
|
|||
from network.uploadthread import UploadThread
|
||||
|
||||
# Helper Functions
|
||||
import helper_generic
|
||||
import helper_threading
|
||||
|
||||
|
||||
def connectToStream(streamNumber):
|
||||
state.streamsInWhichIAmParticipating.append(streamNumber)
|
||||
selfInitiatedConnections[streamNumber] = {}
|
||||
|
@ -150,6 +153,37 @@ def _fixSocket():
|
|||
socket.IPV6_V6ONLY = 27
|
||||
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
"""Single handler for any signal sent to pybitmessage"""
|
||||
process = multiprocessing.current_process()
|
||||
thread = threading.current_thread()
|
||||
logger.error(
|
||||
'Got signal %i in %s/%s',
|
||||
signum, process.name, thread.name
|
||||
)
|
||||
if process.name == "RegExParser":
|
||||
# on Windows this isn't triggered, but it's fine,
|
||||
# it has its own process termination thing
|
||||
raise SystemExit
|
||||
if "PoolWorker" in process.name:
|
||||
raise SystemExit
|
||||
if thread.name not in ("PyBitmessage", "MainThread"):
|
||||
return
|
||||
logger.error("Got signal %i", signum)
|
||||
# there are possible non-UI variants to run bitmessage which should shutdown
|
||||
# especially test-mode
|
||||
if shared.thisapp.daemon or not state.enableGUI:
|
||||
shutdown.doCleanShutdown()
|
||||
else:
|
||||
print('# Thread: %s(%d)' % (thread.name, thread.ident))
|
||||
for filename, lineno, name, line in traceback.extract_stack(frame):
|
||||
print('File: "%s", line %d, in %s' % (filename, lineno, name))
|
||||
if line:
|
||||
print(' %s' % line.strip())
|
||||
print('Unfortunately you cannot use Ctrl+C when running the UI'
|
||||
' because the UI captures the signal.')
|
||||
|
||||
|
||||
# This is a list of current connections (the thread pointers at least)
|
||||
selfInitiatedConnections = {}
|
||||
|
||||
|
@ -437,8 +471,8 @@ class Main:
|
|||
os.kill(grandfatherPid, signal.SIGTERM)
|
||||
|
||||
def setSignalHandler(self):
|
||||
signal.signal(signal.SIGINT, helper_generic.signal_handler)
|
||||
signal.signal(signal.SIGTERM, helper_generic.signal_handler)
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
# signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
def usage(self):
|
||||
|
|
|
@ -9,6 +9,7 @@ import random
|
|||
import string
|
||||
import sys
|
||||
import textwrap
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
from sqlite3 import register_adapter
|
||||
|
@ -44,7 +45,6 @@ from account import (
|
|||
getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount,
|
||||
GatewayAccount, MailchuckAccount, AccountColor)
|
||||
import dialogs
|
||||
from helper_generic import powQueueSize
|
||||
from network.stats import pendingDownload, pendingUpload
|
||||
from uisignaler import UISignaler
|
||||
import knownnodes
|
||||
|
@ -107,6 +107,19 @@ def change_translation(newlocale):
|
|||
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"""
|
||||
queue_len = queues.workerQueue.qsize()
|
||||
for thread in threading.enumerate():
|
||||
try:
|
||||
if thread.name == "singleWorker":
|
||||
queue_len += thread.busy
|
||||
except Exception as err:
|
||||
logger.info('Thread error %s', err)
|
||||
return queue_len
|
||||
|
||||
|
||||
class MyForm(settingsmixin.SMainWindow):
|
||||
|
||||
# the last time that a message arrival sound was played
|
||||
|
@ -117,6 +130,7 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
|
||||
REPLY_TYPE_SENDER = 0
|
||||
REPLY_TYPE_CHAN = 1
|
||||
REPLY_TYPE_UPD = 2
|
||||
|
||||
def init_file_menu(self):
|
||||
QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL(
|
||||
|
@ -368,8 +382,6 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
self.on_context_menuSubscriptions)
|
||||
|
||||
def init_sent_popup_menu(self, connectSignal=True):
|
||||
# Popup menu for the Sent page
|
||||
self.ui.sentContextMenuToolbar = QtGui.QToolBar()
|
||||
# Actions
|
||||
self.actionTrashSentMessage = self.ui.sentContextMenuToolbar.addAction(
|
||||
_translate(
|
||||
|
@ -381,6 +393,9 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
self.actionForceSend = self.ui.sentContextMenuToolbar.addAction(
|
||||
_translate(
|
||||
"MainWindow", "Force send"), self.on_action_ForceSend)
|
||||
self.actionSentReply = self.ui.sentContextMenuToolbar.addAction(
|
||||
_translate("MainWindow", "Send update"),
|
||||
self.on_action_SentReply)
|
||||
# self.popMenuSent = QtGui.QMenu( self )
|
||||
# self.popMenuSent.addAction( self.actionSentClipboard )
|
||||
# self.popMenuSent.addAction( self.actionTrashSentMessage )
|
||||
|
@ -2656,15 +2671,13 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
tableWidget.item(i, 3).setFont(font)
|
||||
|
||||
markread = sqlExecuteChunked(
|
||||
"UPDATE %s SET read = 1 WHERE %s IN({0}) AND read=0" % (
|
||||
('sent', 'ackdata') if self.getCurrentFolder() == 'sent'
|
||||
else ('inbox', 'msgid')
|
||||
), idCount, *msgids
|
||||
"UPDATE inbox SET read = 1 WHERE msgid IN({0}) AND read=0",
|
||||
idCount, *msgids
|
||||
)
|
||||
|
||||
if markread > 0:
|
||||
self.propagateUnreadCount()
|
||||
# addressAtCurrentRow, self.getCurrentFolder(), None, 0)
|
||||
# addressAtCurrentRow, self.getCurrentFolder(), None, 0)
|
||||
|
||||
def click_NewAddressDialog(self):
|
||||
dialogs.NewAddressDialog(self)
|
||||
|
@ -2998,48 +3011,73 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
|
||||
def on_action_InboxReplyChan(self):
|
||||
self.on_action_InboxReply(self.REPLY_TYPE_CHAN)
|
||||
|
||||
def on_action_InboxReply(self, replyType = None):
|
||||
|
||||
def on_action_SentReply(self):
|
||||
self.on_action_InboxReply(self.REPLY_TYPE_UPD)
|
||||
|
||||
def on_action_InboxReply(self, reply_type=None):
|
||||
"""Handle any reply action depending on reply_type"""
|
||||
# pylint: disable=too-many-locals
|
||||
tableWidget = self.getCurrentMessagelist()
|
||||
if not tableWidget:
|
||||
return
|
||||
|
||||
if replyType is None:
|
||||
replyType = self.REPLY_TYPE_SENDER
|
||||
|
||||
|
||||
if reply_type is None:
|
||||
reply_type = self.REPLY_TYPE_SENDER
|
||||
|
||||
# save this to return back after reply is done
|
||||
self.replyFromTab = self.ui.tabWidget.currentIndex()
|
||||
|
||||
|
||||
column_to = 1 if reply_type == self.REPLY_TYPE_UPD else 0
|
||||
column_from = 0 if reply_type == self.REPLY_TYPE_UPD else 1
|
||||
|
||||
currentInboxRow = tableWidget.currentRow()
|
||||
toAddressAtCurrentInboxRow = tableWidget.item(
|
||||
currentInboxRow, 0).address
|
||||
currentInboxRow, column_to).address
|
||||
acct = accountClass(toAddressAtCurrentInboxRow)
|
||||
fromAddressAtCurrentInboxRow = tableWidget.item(
|
||||
currentInboxRow, 1).address
|
||||
currentInboxRow, column_from).address
|
||||
msgid = str(tableWidget.item(
|
||||
currentInboxRow, 3).data(QtCore.Qt.UserRole).toPyObject())
|
||||
queryreturn = sqlQuery(
|
||||
'''select message from inbox where msgid=?''', msgid)
|
||||
"SELECT message FROM inbox WHERE msgid=?", msgid
|
||||
) or sqlQuery("SELECT message FROM sent WHERE ackdata=?", msgid)
|
||||
if queryreturn != []:
|
||||
for row in queryreturn:
|
||||
messageAtCurrentInboxRow, = row
|
||||
acct.parseMessage(toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow, tableWidget.item(currentInboxRow, 2).subject, messageAtCurrentInboxRow)
|
||||
acct.parseMessage(
|
||||
toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow,
|
||||
tableWidget.item(currentInboxRow, 2).subject,
|
||||
messageAtCurrentInboxRow)
|
||||
widget = {
|
||||
'subject': self.ui.lineEditSubject,
|
||||
'from': self.ui.comboBoxSendFrom,
|
||||
'message': self.ui.textEditMessage
|
||||
}
|
||||
|
||||
if toAddressAtCurrentInboxRow == str_broadcast_subscribers:
|
||||
self.ui.tabWidgetSend.setCurrentIndex(
|
||||
self.ui.tabWidgetSend.indexOf(self.ui.sendDirect)
|
||||
)
|
||||
# toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
|
||||
elif not BMConfigParser().has_section(toAddressAtCurrentInboxRow):
|
||||
QtGui.QMessageBox.information(self, _translate("MainWindow", "Address is gone"), _translate(
|
||||
"MainWindow", "Bitmessage cannot find your address %1. Perhaps you removed it?").arg(toAddressAtCurrentInboxRow), QtGui.QMessageBox.Ok)
|
||||
elif not BMConfigParser().getboolean(toAddressAtCurrentInboxRow, 'enabled'):
|
||||
QtGui.QMessageBox.information(self, _translate("MainWindow", "Address disabled"), _translate(
|
||||
"MainWindow", "Error: The address from which you are trying to send is disabled. You\'ll have to enable it on the \'Your Identities\' tab before using it."), QtGui.QMessageBox.Ok)
|
||||
QtGui.QMessageBox.information(
|
||||
self, _translate("MainWindow", "Address is gone"),
|
||||
_translate(
|
||||
"MainWindow",
|
||||
"Bitmessage cannot find your address %1. Perhaps you"
|
||||
" removed it?"
|
||||
).arg(toAddressAtCurrentInboxRow), QtGui.QMessageBox.Ok)
|
||||
elif not BMConfigParser().getboolean(
|
||||
toAddressAtCurrentInboxRow, 'enabled'):
|
||||
QtGui.QMessageBox.information(
|
||||
self, _translate("MainWindow", "Address disabled"),
|
||||
_translate(
|
||||
"MainWindow",
|
||||
"Error: The address from which you are trying to send"
|
||||
" is disabled. You\'ll have to enable it on the"
|
||||
" \'Your Identities\' tab before using it."
|
||||
), QtGui.QMessageBox.Ok)
|
||||
else:
|
||||
self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(toAddressAtCurrentInboxRow)
|
||||
broadcast_tab_index = self.ui.tabWidgetSend.indexOf(
|
||||
|
@ -3053,28 +3091,44 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
}
|
||||
self.ui.tabWidgetSend.setCurrentIndex(broadcast_tab_index)
|
||||
toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
|
||||
if fromAddressAtCurrentInboxRow == tableWidget.item(currentInboxRow, 1).label or (
|
||||
isinstance(acct, GatewayAccount) and fromAddressAtCurrentInboxRow == acct.relayAddress):
|
||||
if fromAddressAtCurrentInboxRow == \
|
||||
tableWidget.item(currentInboxRow, column_from).label or (
|
||||
isinstance(acct, GatewayAccount) and
|
||||
fromAddressAtCurrentInboxRow == acct.relayAddress):
|
||||
self.ui.lineEditTo.setText(str(acct.fromAddress))
|
||||
else:
|
||||
self.ui.lineEditTo.setText(tableWidget.item(currentInboxRow, 1).label + " <" + str(acct.fromAddress) + ">")
|
||||
|
||||
# If the previous message was to a chan then we should send our reply to the chan rather than to the particular person who sent the message.
|
||||
if acct.type == AccountMixin.CHAN and replyType == self.REPLY_TYPE_CHAN:
|
||||
logger.debug('original sent to a chan. Setting the to address in the reply to the chan address.')
|
||||
if toAddressAtCurrentInboxRow == tableWidget.item(currentInboxRow, 0).label:
|
||||
self.ui.lineEditTo.setText(
|
||||
tableWidget.item(currentInboxRow, column_from).label +
|
||||
" <" + str(acct.fromAddress) + ">"
|
||||
)
|
||||
|
||||
# If the previous message was to a chan then we should send our
|
||||
# reply to the chan rather than to the particular person who sent
|
||||
# the message.
|
||||
if acct.type == AccountMixin.CHAN and reply_type == self.REPLY_TYPE_CHAN:
|
||||
logger.debug(
|
||||
'Original sent to a chan. Setting the to address in the'
|
||||
' reply to the chan address.')
|
||||
if toAddressAtCurrentInboxRow == \
|
||||
tableWidget.item(currentInboxRow, column_to).label:
|
||||
self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow))
|
||||
else:
|
||||
self.ui.lineEditTo.setText(tableWidget.item(currentInboxRow, 0).label + " <" + str(acct.toAddress) + ">")
|
||||
|
||||
self.ui.lineEditTo.setText(
|
||||
tableWidget.item(currentInboxRow, column_to).label +
|
||||
" <" + str(acct.toAddress) + ">"
|
||||
)
|
||||
|
||||
self.setSendFromComboBox(toAddressAtCurrentInboxRow)
|
||||
|
||||
quotedText = self.quoted_text(unicode(messageAtCurrentInboxRow, 'utf-8', 'replace'))
|
||||
|
||||
quotedText = self.quoted_text(
|
||||
unicode(messageAtCurrentInboxRow, 'utf-8', 'replace'))
|
||||
widget['message'].setPlainText(quotedText)
|
||||
if acct.subject[0:3] in ['Re:', 'RE:']:
|
||||
widget['subject'].setText(tableWidget.item(currentInboxRow, 2).label)
|
||||
widget['subject'].setText(
|
||||
tableWidget.item(currentInboxRow, 2).label)
|
||||
else:
|
||||
widget['subject'].setText('Re: ' + tableWidget.item(currentInboxRow, 2).label)
|
||||
widget['subject'].setText(
|
||||
'Re: ' + tableWidget.item(currentInboxRow, 2).label)
|
||||
self.ui.tabWidget.setCurrentIndex(
|
||||
self.ui.tabWidget.indexOf(self.ui.send)
|
||||
)
|
||||
|
@ -3459,11 +3513,14 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
for plugin in self.menu_plugins['address']:
|
||||
self.popMenuSubscriptions.addAction(plugin)
|
||||
self.popMenuSubscriptions.addSeparator()
|
||||
self.popMenuSubscriptions.addAction(self.actionMarkAllRead)
|
||||
if self.getCurrentFolder() != 'sent':
|
||||
self.popMenuSubscriptions.addAction(self.actionMarkAllRead)
|
||||
if self.popMenuSubscriptions.isEmpty():
|
||||
return
|
||||
self.popMenuSubscriptions.exec_(
|
||||
self.ui.treeWidgetSubscriptions.mapToGlobal(point))
|
||||
|
||||
def widgetConvert (self, widget):
|
||||
def widgetConvert(self, widget):
|
||||
if widget == self.ui.tableWidgetInbox:
|
||||
return self.ui.treeWidgetYourIdentities
|
||||
elif widget == self.ui.tableWidgetInboxSubscriptions:
|
||||
|
@ -3874,8 +3931,10 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
for plugin in self.menu_plugins['address']:
|
||||
self.popMenuYourIdentities.addAction(plugin)
|
||||
self.popMenuYourIdentities.addSeparator()
|
||||
self.popMenuYourIdentities.addAction(self.actionMarkAllRead)
|
||||
|
||||
if self.getCurrentFolder() != 'sent':
|
||||
self.popMenuYourIdentities.addAction(self.actionMarkAllRead)
|
||||
if self.popMenuYourIdentities.isEmpty():
|
||||
return
|
||||
self.popMenuYourIdentities.exec_(
|
||||
self.ui.treeWidgetYourIdentities.mapToGlobal(point))
|
||||
|
||||
|
@ -3899,7 +3958,10 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
for plugin in self.menu_plugins['address']:
|
||||
self.popMenu.addAction(plugin)
|
||||
self.popMenu.addSeparator()
|
||||
self.popMenu.addAction(self.actionMarkAllRead)
|
||||
if self.getCurrentFolder() != 'sent':
|
||||
self.popMenu.addAction(self.actionMarkAllRead)
|
||||
if self.popMenu.isEmpty():
|
||||
return
|
||||
self.popMenu.exec_(
|
||||
self.ui.treeWidgetChans.mapToGlobal(point))
|
||||
|
||||
|
@ -3943,6 +4005,7 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
self.popMenuSent = QtGui.QMenu(self)
|
||||
self.popMenuSent.addAction(self.actionSentClipboard)
|
||||
self.popMenuSent.addAction(self.actionTrashSentMessage)
|
||||
self.popMenuSent.addAction(self.actionSentReply)
|
||||
|
||||
# Check to see if this item is toodifficult and display an additional
|
||||
# menu option (Force Send) if it is.
|
||||
|
|
|
@ -651,6 +651,10 @@ class Ui_MainWindow(object):
|
|||
MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage)
|
||||
MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription)
|
||||
|
||||
# Popup menu actions container for the Sent page
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.sentContextMenuToolbar = QtGui.QToolBar()
|
||||
|
||||
def updateNetworkSwitchMenuLabel(self, dontconnect=None):
|
||||
if dontconnect is None:
|
||||
dontconnect = BMConfigParser().safeGetBoolean(
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
"""
|
||||
Helper Generic perform generic operations for threading.
|
||||
|
||||
Also perform some conversion operations.
|
||||
"""
|
||||
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import multiprocessing
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
import shared
|
||||
import state
|
||||
import queues
|
||||
import shutdown
|
||||
from debug import logger
|
||||
|
||||
|
||||
def powQueueSize():
|
||||
curWorkerQueue = queues.workerQueue.qsize()
|
||||
for thread in threading.enumerate():
|
||||
try:
|
||||
if thread.name == "singleWorker":
|
||||
curWorkerQueue += thread.busy
|
||||
except Exception as err:
|
||||
logger.info('Thread error %s', err)
|
||||
return curWorkerQueue
|
||||
|
||||
|
||||
def convertIntToString(n):
|
||||
a = __builtins__.hex(n)
|
||||
if a[-1:] == 'L':
|
||||
a = a[:-1]
|
||||
if (len(a) % 2) == 0:
|
||||
return unhexlify(a[2:])
|
||||
else:
|
||||
return unhexlify('0' + a[2:])
|
||||
|
||||
|
||||
def convertStringToInt(s):
|
||||
return int(hexlify(s), 16)
|
||||
|
||||
|
||||
def allThreadTraceback(frame):
|
||||
id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
|
||||
code = []
|
||||
for threadId, stack in sys._current_frames().items():
|
||||
code.append(
|
||||
'\n# Thread: %s(%d)' % (id2name.get(threadId, ''), threadId))
|
||||
for filename, lineno, name, line in traceback.extract_stack(stack):
|
||||
code.append(
|
||||
'File: "%s", line %d, in %s' % (filename, lineno, name))
|
||||
if line:
|
||||
code.append(' %s' % (line.strip()))
|
||||
print('\n'.join(code))
|
||||
|
||||
|
||||
def signal_handler(signal, frame):
|
||||
process = multiprocessing.current_process()
|
||||
logger.error(
|
||||
'Got signal %i in %s/%s',
|
||||
signal, process.name, threading.current_thread().name
|
||||
)
|
||||
if process.name == "RegExParser":
|
||||
# on Windows this isn't triggered, but it's fine,
|
||||
# it has its own process termination thing
|
||||
raise SystemExit
|
||||
if "PoolWorker" in process.name:
|
||||
raise SystemExit
|
||||
if threading.current_thread().name not in ("PyBitmessage", "MainThread"):
|
||||
return
|
||||
logger.error("Got signal %i", signal)
|
||||
if shared.thisapp.daemon or not state.enableGUI: # FIXME redundant?
|
||||
shutdown.doCleanShutdown()
|
||||
else:
|
||||
allThreadTraceback(frame)
|
||||
print('Unfortunately you cannot use Ctrl+C when running the UI'
|
||||
' because the UI captures the signal.')
|
||||
|
||||
|
||||
def isHostInPrivateIPRange(host):
|
||||
if ":" in host: # IPv6
|
||||
hostAddr = socket.inet_pton(socket.AF_INET6, host)
|
||||
if hostAddr == ('\x00' * 15) + '\x01':
|
||||
return False
|
||||
if hostAddr[0] == '\xFE' and (ord(hostAddr[1]) & 0xc0) == 0x80:
|
||||
return False
|
||||
if (ord(hostAddr[0]) & 0xfe) == 0xfc:
|
||||
return False
|
||||
elif ".onion" not in host:
|
||||
if host[:3] == '10.':
|
||||
return True
|
||||
if host[:4] == '172.':
|
||||
if host[6] == '.':
|
||||
if int(host[4:6]) >= 16 and int(host[4:6]) <= 31:
|
||||
return True
|
||||
if host[:8] == '192.168.':
|
||||
return True
|
||||
# Multicast
|
||||
if host[:3] >= 224 and host[:3] <= 239 and host[4] == '.':
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def addDataPadding(data, desiredMsgLength=12, paddingChar='\x00'):
|
||||
return data + paddingChar * (desiredMsgLength - len(data))
|
|
@ -130,36 +130,30 @@ class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instanc
|
|||
# We are going to share a maximum number of 1000 addrs (per overlapping
|
||||
# stream) with our peer. 500 from overlapping streams, 250 from the
|
||||
# left child stream, and 250 from the right child stream.
|
||||
maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500)
|
||||
maxAddrCount = BMConfigParser().safeGetInt(
|
||||
"bitmessagesettings", "maxaddrperstreamsend", 500)
|
||||
|
||||
# init
|
||||
templist = []
|
||||
addrs = {}
|
||||
for stream in self.streams:
|
||||
with knownnodes.knownNodesLock:
|
||||
if knownnodes.knownNodes[stream]:
|
||||
filtered = {k: v for k, v in knownnodes.knownNodes[stream].items()
|
||||
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
||||
elemCount = len(filtered)
|
||||
if elemCount > maxAddrCount:
|
||||
elemCount = maxAddrCount
|
||||
for n, s in enumerate((stream, stream * 2, stream * 2 + 1)):
|
||||
nodes = knownnodes.knownNodes.get(s)
|
||||
if not nodes:
|
||||
continue
|
||||
# only if more recent than 3 hours
|
||||
addrs[stream] = helper_random.randomsample(filtered.items(), elemCount)
|
||||
# sent 250 only if the remote isn't interested in it
|
||||
if knownnodes.knownNodes[stream * 2] and stream not in self.streams:
|
||||
filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2].items()
|
||||
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
||||
elemCount = len(filtered)
|
||||
if elemCount > maxAddrCount / 2:
|
||||
elemCount = int(maxAddrCount / 2)
|
||||
addrs[stream * 2] = helper_random.randomsample(filtered.items(), elemCount)
|
||||
if knownnodes.knownNodes[(stream * 2) + 1] and stream not in self.streams:
|
||||
filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2 + 1].items()
|
||||
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
||||
elemCount = len(filtered)
|
||||
if elemCount > maxAddrCount / 2:
|
||||
elemCount = int(maxAddrCount / 2)
|
||||
addrs[stream * 2 + 1] = helper_random.randomsample(filtered.items(), elemCount)
|
||||
# and having positive or neutral rating
|
||||
filtered = [
|
||||
(k, v) for k, v in nodes.iteritems()
|
||||
if v["lastseen"] > int(time.time()) -
|
||||
shared.maximumAgeOfNodesThatIAdvertiseToOthers and
|
||||
v["rating"] >= 0
|
||||
]
|
||||
# sent 250 only if the remote isn't interested in it
|
||||
elemCount = min(
|
||||
len(filtered),
|
||||
maxAddrCount / 2 if n else maxAddrCount)
|
||||
addrs[s] = helper_random.randomsample(filtered, elemCount)
|
||||
for substream in addrs:
|
||||
for peer, params in addrs[substream]:
|
||||
templist.append((substream, peer, params["lastseen"]))
|
||||
|
|
|
@ -89,6 +89,24 @@ class TestProcessProto(unittest.TestCase):
|
|||
len(self.process.threads()), self._threads_count)
|
||||
|
||||
|
||||
class TestProcessShutdown(TestProcessProto):
|
||||
"""Separate test case for SIGTERM"""
|
||||
def test_shutdown(self):
|
||||
"""Send to pybitmessage SIGTERM and ensure it stopped"""
|
||||
self.process.send_signal(signal.SIGTERM)
|
||||
try:
|
||||
# longer wait time because it's not a benchmark
|
||||
self.process.wait(10)
|
||||
except psutil.TimeoutExpired:
|
||||
self.fail(
|
||||
'%s has not stopped in 10 sec' % ' '.join(self._process_cmd))
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Special teardown because pybitmessage is already stopped"""
|
||||
cls._cleanup_files()
|
||||
|
||||
|
||||
class TestProcess(TestProcessProto):
|
||||
"""A test case for pybitmessage process"""
|
||||
def test_process_name(self):
|
||||
|
|
Loading…
Reference in New Issue
Block a user