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 COPYING
|
||||||
include README.md
|
include README.md
|
||||||
|
include requirements.txt
|
||||||
recursive-include desktop *
|
recursive-include desktop *
|
||||||
|
|
|
@ -5,23 +5,11 @@ src/addresses.py
|
||||||
"""
|
"""
|
||||||
# pylint: disable=redefined-outer-name,inconsistent-return-statements
|
# pylint: disable=redefined-outer-name,inconsistent-return-statements
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
import hashlib
|
import hashlib
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
from struct import pack, unpack
|
from struct import pack, unpack
|
||||||
|
|
||||||
from debug import logger
|
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"
|
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||||
|
@ -288,7 +276,10 @@ def addBMIfNotPresent(address):
|
||||||
return address if address[:3] == 'BM-' else 'BM-' + address
|
return address if address[:3] == 'BM-' else 'BM-' + address
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: make test case
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
from pyelliptic import arithmetic
|
||||||
|
|
||||||
print(
|
print(
|
||||||
'\nLet us make an address from scratch. Suppose we generate two'
|
'\nLet us make an address from scratch. Suppose we generate two'
|
||||||
' random 32 byte values and call the first one the signing key'
|
' random 32 byte values and call the first one the signing key'
|
||||||
|
|
|
@ -661,8 +661,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
BMConfigParser().remove_section(address)
|
BMConfigParser().remove_section(address)
|
||||||
with open(state.appdata + 'keys.dat', 'wb') as configfile:
|
with open(state.appdata + 'keys.dat', 'wb') as configfile:
|
||||||
BMConfigParser().write(configfile)
|
BMConfigParser().write(configfile)
|
||||||
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
|
queues.UISignalQueue.put(('writeNewAddressToTable', ('', '', '')))
|
||||||
queues.UISignalQueue.put(('rerenderMessagelistToLabels', ''))
|
|
||||||
shared.reloadMyAddressHashes()
|
shared.reloadMyAddressHashes()
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,13 @@ depends.check_dependencies()
|
||||||
|
|
||||||
import ctypes
|
import ctypes
|
||||||
import getopt
|
import getopt
|
||||||
|
import multiprocessing
|
||||||
# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
|
# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
from struct import pack
|
from struct import pack
|
||||||
|
|
||||||
from helper_startup import (
|
from helper_startup import (
|
||||||
|
@ -38,7 +41,7 @@ import shared
|
||||||
import knownnodes
|
import knownnodes
|
||||||
import state
|
import state
|
||||||
import shutdown
|
import shutdown
|
||||||
import threading
|
from debug import logger
|
||||||
|
|
||||||
# Classes
|
# Classes
|
||||||
from class_sqlThread import sqlThread
|
from class_sqlThread import sqlThread
|
||||||
|
@ -61,9 +64,9 @@ from network.downloadthread import DownloadThread
|
||||||
from network.uploadthread import UploadThread
|
from network.uploadthread import UploadThread
|
||||||
|
|
||||||
# Helper Functions
|
# Helper Functions
|
||||||
import helper_generic
|
|
||||||
import helper_threading
|
import helper_threading
|
||||||
|
|
||||||
|
|
||||||
def connectToStream(streamNumber):
|
def connectToStream(streamNumber):
|
||||||
state.streamsInWhichIAmParticipating.append(streamNumber)
|
state.streamsInWhichIAmParticipating.append(streamNumber)
|
||||||
selfInitiatedConnections[streamNumber] = {}
|
selfInitiatedConnections[streamNumber] = {}
|
||||||
|
@ -150,6 +153,37 @@ def _fixSocket():
|
||||||
socket.IPV6_V6ONLY = 27
|
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)
|
# This is a list of current connections (the thread pointers at least)
|
||||||
selfInitiatedConnections = {}
|
selfInitiatedConnections = {}
|
||||||
|
|
||||||
|
@ -437,8 +471,8 @@ class Main:
|
||||||
os.kill(grandfatherPid, signal.SIGTERM)
|
os.kill(grandfatherPid, signal.SIGTERM)
|
||||||
|
|
||||||
def setSignalHandler(self):
|
def setSignalHandler(self):
|
||||||
signal.signal(signal.SIGINT, helper_generic.signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
signal.signal(signal.SIGTERM, helper_generic.signal_handler)
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
# signal.signal(signal.SIGINT, signal.SIG_DFL)
|
# signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||||
|
|
||||||
def usage(self):
|
def usage(self):
|
||||||
|
|
|
@ -9,6 +9,7 @@ import random
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from sqlite3 import register_adapter
|
from sqlite3 import register_adapter
|
||||||
|
@ -44,7 +45,6 @@ from account import (
|
||||||
getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount,
|
getSortedAccounts, getSortedSubscriptions, accountClass, BMAccount,
|
||||||
GatewayAccount, MailchuckAccount, AccountColor)
|
GatewayAccount, MailchuckAccount, AccountColor)
|
||||||
import dialogs
|
import dialogs
|
||||||
from helper_generic import powQueueSize
|
|
||||||
from network.stats import pendingDownload, pendingUpload
|
from network.stats import pendingDownload, pendingUpload
|
||||||
from uisignaler import UISignaler
|
from uisignaler import UISignaler
|
||||||
import knownnodes
|
import knownnodes
|
||||||
|
@ -107,6 +107,19 @@ def change_translation(newlocale):
|
||||||
logger.error("Failed to set locale to %s", lang, exc_info=True)
|
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):
|
class MyForm(settingsmixin.SMainWindow):
|
||||||
|
|
||||||
# the last time that a message arrival sound was played
|
# the last time that a message arrival sound was played
|
||||||
|
@ -117,6 +130,7 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
|
|
||||||
REPLY_TYPE_SENDER = 0
|
REPLY_TYPE_SENDER = 0
|
||||||
REPLY_TYPE_CHAN = 1
|
REPLY_TYPE_CHAN = 1
|
||||||
|
REPLY_TYPE_UPD = 2
|
||||||
|
|
||||||
def init_file_menu(self):
|
def init_file_menu(self):
|
||||||
QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL(
|
QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL(
|
||||||
|
@ -368,8 +382,6 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
self.on_context_menuSubscriptions)
|
self.on_context_menuSubscriptions)
|
||||||
|
|
||||||
def init_sent_popup_menu(self, connectSignal=True):
|
def init_sent_popup_menu(self, connectSignal=True):
|
||||||
# Popup menu for the Sent page
|
|
||||||
self.ui.sentContextMenuToolbar = QtGui.QToolBar()
|
|
||||||
# Actions
|
# Actions
|
||||||
self.actionTrashSentMessage = self.ui.sentContextMenuToolbar.addAction(
|
self.actionTrashSentMessage = self.ui.sentContextMenuToolbar.addAction(
|
||||||
_translate(
|
_translate(
|
||||||
|
@ -381,6 +393,9 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
self.actionForceSend = self.ui.sentContextMenuToolbar.addAction(
|
self.actionForceSend = self.ui.sentContextMenuToolbar.addAction(
|
||||||
_translate(
|
_translate(
|
||||||
"MainWindow", "Force send"), self.on_action_ForceSend)
|
"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 = QtGui.QMenu( self )
|
||||||
# self.popMenuSent.addAction( self.actionSentClipboard )
|
# self.popMenuSent.addAction( self.actionSentClipboard )
|
||||||
# self.popMenuSent.addAction( self.actionTrashSentMessage )
|
# self.popMenuSent.addAction( self.actionTrashSentMessage )
|
||||||
|
@ -2656,15 +2671,13 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
tableWidget.item(i, 3).setFont(font)
|
tableWidget.item(i, 3).setFont(font)
|
||||||
|
|
||||||
markread = sqlExecuteChunked(
|
markread = sqlExecuteChunked(
|
||||||
"UPDATE %s SET read = 1 WHERE %s IN({0}) AND read=0" % (
|
"UPDATE inbox SET read = 1 WHERE msgid IN({0}) AND read=0",
|
||||||
('sent', 'ackdata') if self.getCurrentFolder() == 'sent'
|
idCount, *msgids
|
||||||
else ('inbox', 'msgid')
|
|
||||||
), idCount, *msgids
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if markread > 0:
|
if markread > 0:
|
||||||
self.propagateUnreadCount()
|
self.propagateUnreadCount()
|
||||||
# addressAtCurrentRow, self.getCurrentFolder(), None, 0)
|
# addressAtCurrentRow, self.getCurrentFolder(), None, 0)
|
||||||
|
|
||||||
def click_NewAddressDialog(self):
|
def click_NewAddressDialog(self):
|
||||||
dialogs.NewAddressDialog(self)
|
dialogs.NewAddressDialog(self)
|
||||||
|
@ -2998,48 +3011,73 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
|
|
||||||
def on_action_InboxReplyChan(self):
|
def on_action_InboxReplyChan(self):
|
||||||
self.on_action_InboxReply(self.REPLY_TYPE_CHAN)
|
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()
|
tableWidget = self.getCurrentMessagelist()
|
||||||
if not tableWidget:
|
if not tableWidget:
|
||||||
return
|
return
|
||||||
|
|
||||||
if replyType is None:
|
if reply_type is None:
|
||||||
replyType = self.REPLY_TYPE_SENDER
|
reply_type = self.REPLY_TYPE_SENDER
|
||||||
|
|
||||||
# save this to return back after reply is done
|
# save this to return back after reply is done
|
||||||
self.replyFromTab = self.ui.tabWidget.currentIndex()
|
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()
|
currentInboxRow = tableWidget.currentRow()
|
||||||
toAddressAtCurrentInboxRow = tableWidget.item(
|
toAddressAtCurrentInboxRow = tableWidget.item(
|
||||||
currentInboxRow, 0).address
|
currentInboxRow, column_to).address
|
||||||
acct = accountClass(toAddressAtCurrentInboxRow)
|
acct = accountClass(toAddressAtCurrentInboxRow)
|
||||||
fromAddressAtCurrentInboxRow = tableWidget.item(
|
fromAddressAtCurrentInboxRow = tableWidget.item(
|
||||||
currentInboxRow, 1).address
|
currentInboxRow, column_from).address
|
||||||
msgid = str(tableWidget.item(
|
msgid = str(tableWidget.item(
|
||||||
currentInboxRow, 3).data(QtCore.Qt.UserRole).toPyObject())
|
currentInboxRow, 3).data(QtCore.Qt.UserRole).toPyObject())
|
||||||
queryreturn = sqlQuery(
|
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 != []:
|
if queryreturn != []:
|
||||||
for row in queryreturn:
|
for row in queryreturn:
|
||||||
messageAtCurrentInboxRow, = row
|
messageAtCurrentInboxRow, = row
|
||||||
acct.parseMessage(toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow, tableWidget.item(currentInboxRow, 2).subject, messageAtCurrentInboxRow)
|
acct.parseMessage(
|
||||||
|
toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow,
|
||||||
|
tableWidget.item(currentInboxRow, 2).subject,
|
||||||
|
messageAtCurrentInboxRow)
|
||||||
widget = {
|
widget = {
|
||||||
'subject': self.ui.lineEditSubject,
|
'subject': self.ui.lineEditSubject,
|
||||||
'from': self.ui.comboBoxSendFrom,
|
'from': self.ui.comboBoxSendFrom,
|
||||||
'message': self.ui.textEditMessage
|
'message': self.ui.textEditMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
if toAddressAtCurrentInboxRow == str_broadcast_subscribers:
|
if toAddressAtCurrentInboxRow == str_broadcast_subscribers:
|
||||||
self.ui.tabWidgetSend.setCurrentIndex(
|
self.ui.tabWidgetSend.setCurrentIndex(
|
||||||
self.ui.tabWidgetSend.indexOf(self.ui.sendDirect)
|
self.ui.tabWidgetSend.indexOf(self.ui.sendDirect)
|
||||||
)
|
)
|
||||||
# toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
|
# toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
|
||||||
elif not BMConfigParser().has_section(toAddressAtCurrentInboxRow):
|
elif not BMConfigParser().has_section(toAddressAtCurrentInboxRow):
|
||||||
QtGui.QMessageBox.information(self, _translate("MainWindow", "Address is gone"), _translate(
|
QtGui.QMessageBox.information(
|
||||||
"MainWindow", "Bitmessage cannot find your address %1. Perhaps you removed it?").arg(toAddressAtCurrentInboxRow), QtGui.QMessageBox.Ok)
|
self, _translate("MainWindow", "Address is gone"),
|
||||||
elif not BMConfigParser().getboolean(toAddressAtCurrentInboxRow, 'enabled'):
|
_translate(
|
||||||
QtGui.QMessageBox.information(self, _translate("MainWindow", "Address disabled"), _translate(
|
"MainWindow",
|
||||||
"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)
|
"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:
|
else:
|
||||||
self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(toAddressAtCurrentInboxRow)
|
self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(toAddressAtCurrentInboxRow)
|
||||||
broadcast_tab_index = self.ui.tabWidgetSend.indexOf(
|
broadcast_tab_index = self.ui.tabWidgetSend.indexOf(
|
||||||
|
@ -3053,28 +3091,44 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
}
|
}
|
||||||
self.ui.tabWidgetSend.setCurrentIndex(broadcast_tab_index)
|
self.ui.tabWidgetSend.setCurrentIndex(broadcast_tab_index)
|
||||||
toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
|
toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
|
||||||
if fromAddressAtCurrentInboxRow == tableWidget.item(currentInboxRow, 1).label or (
|
if fromAddressAtCurrentInboxRow == \
|
||||||
isinstance(acct, GatewayAccount) and fromAddressAtCurrentInboxRow == acct.relayAddress):
|
tableWidget.item(currentInboxRow, column_from).label or (
|
||||||
|
isinstance(acct, GatewayAccount) and
|
||||||
|
fromAddressAtCurrentInboxRow == acct.relayAddress):
|
||||||
self.ui.lineEditTo.setText(str(acct.fromAddress))
|
self.ui.lineEditTo.setText(str(acct.fromAddress))
|
||||||
else:
|
else:
|
||||||
self.ui.lineEditTo.setText(tableWidget.item(currentInboxRow, 1).label + " <" + str(acct.fromAddress) + ">")
|
self.ui.lineEditTo.setText(
|
||||||
|
tableWidget.item(currentInboxRow, column_from).label +
|
||||||
# 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.
|
" <" + str(acct.fromAddress) + ">"
|
||||||
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:
|
# 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))
|
self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow))
|
||||||
else:
|
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)
|
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)
|
widget['message'].setPlainText(quotedText)
|
||||||
if acct.subject[0:3] in ['Re:', 'RE:']:
|
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:
|
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.setCurrentIndex(
|
||||||
self.ui.tabWidget.indexOf(self.ui.send)
|
self.ui.tabWidget.indexOf(self.ui.send)
|
||||||
)
|
)
|
||||||
|
@ -3459,11 +3513,14 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
for plugin in self.menu_plugins['address']:
|
for plugin in self.menu_plugins['address']:
|
||||||
self.popMenuSubscriptions.addAction(plugin)
|
self.popMenuSubscriptions.addAction(plugin)
|
||||||
self.popMenuSubscriptions.addSeparator()
|
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.popMenuSubscriptions.exec_(
|
||||||
self.ui.treeWidgetSubscriptions.mapToGlobal(point))
|
self.ui.treeWidgetSubscriptions.mapToGlobal(point))
|
||||||
|
|
||||||
def widgetConvert (self, widget):
|
def widgetConvert(self, widget):
|
||||||
if widget == self.ui.tableWidgetInbox:
|
if widget == self.ui.tableWidgetInbox:
|
||||||
return self.ui.treeWidgetYourIdentities
|
return self.ui.treeWidgetYourIdentities
|
||||||
elif widget == self.ui.tableWidgetInboxSubscriptions:
|
elif widget == self.ui.tableWidgetInboxSubscriptions:
|
||||||
|
@ -3874,8 +3931,10 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
for plugin in self.menu_plugins['address']:
|
for plugin in self.menu_plugins['address']:
|
||||||
self.popMenuYourIdentities.addAction(plugin)
|
self.popMenuYourIdentities.addAction(plugin)
|
||||||
self.popMenuYourIdentities.addSeparator()
|
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.popMenuYourIdentities.exec_(
|
||||||
self.ui.treeWidgetYourIdentities.mapToGlobal(point))
|
self.ui.treeWidgetYourIdentities.mapToGlobal(point))
|
||||||
|
|
||||||
|
@ -3899,7 +3958,10 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
for plugin in self.menu_plugins['address']:
|
for plugin in self.menu_plugins['address']:
|
||||||
self.popMenu.addAction(plugin)
|
self.popMenu.addAction(plugin)
|
||||||
self.popMenu.addSeparator()
|
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.popMenu.exec_(
|
||||||
self.ui.treeWidgetChans.mapToGlobal(point))
|
self.ui.treeWidgetChans.mapToGlobal(point))
|
||||||
|
|
||||||
|
@ -3943,6 +4005,7 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
self.popMenuSent = QtGui.QMenu(self)
|
self.popMenuSent = QtGui.QMenu(self)
|
||||||
self.popMenuSent.addAction(self.actionSentClipboard)
|
self.popMenuSent.addAction(self.actionSentClipboard)
|
||||||
self.popMenuSent.addAction(self.actionTrashSentMessage)
|
self.popMenuSent.addAction(self.actionTrashSentMessage)
|
||||||
|
self.popMenuSent.addAction(self.actionSentReply)
|
||||||
|
|
||||||
# Check to see if this item is toodifficult and display an additional
|
# Check to see if this item is toodifficult and display an additional
|
||||||
# menu option (Force Send) if it is.
|
# menu option (Force Send) if it is.
|
||||||
|
|
|
@ -651,6 +651,10 @@ class Ui_MainWindow(object):
|
||||||
MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage)
|
MainWindow.setTabOrder(self.lineEditSubject, self.textEditMessage)
|
||||||
MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription)
|
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):
|
def updateNetworkSwitchMenuLabel(self, dontconnect=None):
|
||||||
if dontconnect is None:
|
if dontconnect is None:
|
||||||
dontconnect = BMConfigParser().safeGetBoolean(
|
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
|
# We are going to share a maximum number of 1000 addrs (per overlapping
|
||||||
# stream) with our peer. 500 from overlapping streams, 250 from the
|
# stream) with our peer. 500 from overlapping streams, 250 from the
|
||||||
# left child stream, and 250 from the right child stream.
|
# left child stream, and 250 from the right child stream.
|
||||||
maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500)
|
maxAddrCount = BMConfigParser().safeGetInt(
|
||||||
|
"bitmessagesettings", "maxaddrperstreamsend", 500)
|
||||||
|
|
||||||
# init
|
|
||||||
templist = []
|
templist = []
|
||||||
addrs = {}
|
addrs = {}
|
||||||
for stream in self.streams:
|
for stream in self.streams:
|
||||||
with knownnodes.knownNodesLock:
|
with knownnodes.knownNodesLock:
|
||||||
if knownnodes.knownNodes[stream]:
|
for n, s in enumerate((stream, stream * 2, stream * 2 + 1)):
|
||||||
filtered = {k: v for k, v in knownnodes.knownNodes[stream].items()
|
nodes = knownnodes.knownNodes.get(s)
|
||||||
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
if not nodes:
|
||||||
elemCount = len(filtered)
|
continue
|
||||||
if elemCount > maxAddrCount:
|
|
||||||
elemCount = maxAddrCount
|
|
||||||
# only if more recent than 3 hours
|
# only if more recent than 3 hours
|
||||||
addrs[stream] = helper_random.randomsample(filtered.items(), elemCount)
|
# and having positive or neutral rating
|
||||||
# sent 250 only if the remote isn't interested in it
|
filtered = [
|
||||||
if knownnodes.knownNodes[stream * 2] and stream not in self.streams:
|
(k, v) for k, v in nodes.iteritems()
|
||||||
filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2].items()
|
if v["lastseen"] > int(time.time()) -
|
||||||
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
shared.maximumAgeOfNodesThatIAdvertiseToOthers and
|
||||||
elemCount = len(filtered)
|
v["rating"] >= 0
|
||||||
if elemCount > maxAddrCount / 2:
|
]
|
||||||
elemCount = int(maxAddrCount / 2)
|
# sent 250 only if the remote isn't interested in it
|
||||||
addrs[stream * 2] = helper_random.randomsample(filtered.items(), elemCount)
|
elemCount = min(
|
||||||
if knownnodes.knownNodes[(stream * 2) + 1] and stream not in self.streams:
|
len(filtered),
|
||||||
filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2 + 1].items()
|
maxAddrCount / 2 if n else maxAddrCount)
|
||||||
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
addrs[s] = helper_random.randomsample(filtered, elemCount)
|
||||||
elemCount = len(filtered)
|
|
||||||
if elemCount > maxAddrCount / 2:
|
|
||||||
elemCount = int(maxAddrCount / 2)
|
|
||||||
addrs[stream * 2 + 1] = helper_random.randomsample(filtered.items(), elemCount)
|
|
||||||
for substream in addrs:
|
for substream in addrs:
|
||||||
for peer, params in addrs[substream]:
|
for peer, params in addrs[substream]:
|
||||||
templist.append((substream, peer, params["lastseen"]))
|
templist.append((substream, peer, params["lastseen"]))
|
||||||
|
|
|
@ -89,6 +89,24 @@ class TestProcessProto(unittest.TestCase):
|
||||||
len(self.process.threads()), self._threads_count)
|
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):
|
class TestProcess(TestProcessProto):
|
||||||
"""A test case for pybitmessage process"""
|
"""A test case for pybitmessage process"""
|
||||||
def test_process_name(self):
|
def test_process_name(self):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user