diff --git a/LICENSE b/LICENSE
index d2afc3c4..06576583 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2012-2016 Jonathan Warren
-Copyright (c) 2013-2016 The Bitmessage Developers
+Copyright (c) 2013-2018 The Bitmessage Developers
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
diff --git a/setup.py b/setup.py
index 12670ed6..ba34f6df 100644
--- a/setup.py
+++ b/setup.py
@@ -112,7 +112,8 @@ if __name__ == "__main__":
zip_safe=False,
entry_points={
'bitmessage.gui.menu': [
- 'address.qrcode = pybitmessage.plugins.menu_qrcode [qrcode]'
+ 'popMenuYourIdentities.qrcode = '
+ 'pybitmessage.plugins.qrcodeui [qrcode]'
],
'bitmessage.notification.message': [
'notify2 = pybitmessage.plugins.notification_notify2'
diff --git a/src/bitmessagecli.py b/src/bitmessagecli.py
index 05f4506e..d7f4e5a9 100644
--- a/src/bitmessagecli.py
+++ b/src/bitmessagecli.py
@@ -375,9 +375,10 @@ def bmSettings(): #Allows the viewing and modification of keys.dat settings.
main()
def validAddress(address):
- address_information = json.loads(api.decodeAddress(address))
+ address_information = api.decodeAddress(address)
+ address_information = eval(address_information)
- if 'success' in str(address_information['status']).lower():
+ if 'success' in str(address_information.get('status')).lower():
return True
else:
return False
@@ -1345,14 +1346,15 @@ def UI(usrInput): #Main user menu
elif usrInput == "addinfo":
tmp_address = userInput('\nEnter the Bitmessage Address.')
- address_information = json.loads(api.decodeAddress(tmp_address))
+ address_information = api.decodeAddress(tmp_address)
+ address_information = eval(address_information)
print '\n------------------------------'
- if 'success' in str(address_information['status']).lower():
+ if 'success' in str(address_information.get('status')).lower():
print ' Valid Address'
- print ' Address Version: %s' % str(address_information['addressVersion'])
- print ' Stream Number: %s' % str(address_information['streamNumber'])
+ print ' Address Version: %s' % str(address_information.get('addressVersion'))
+ print ' Stream Number: %s' % str(address_information.get('streamNumber'))
else:
print ' Invalid Address !'
diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py
index 001dc053..7bbd7dd9 100644
--- a/src/bitmessageqt/__init__.py
+++ b/src/bitmessageqt/__init__.py
@@ -47,7 +47,9 @@ from account import (
GatewayAccount, MailchuckAccount, AccountColor)
import dialogs
from helper_generic import powQueueSize
-from network.stats import pendingDownload, pendingUpload
+from inventory import (
+ PendingDownloadQueue, PendingUpload,
+ PendingUploadDeadlineException)
from uisignaler import UISignaler
import knownnodes
import paths
@@ -255,18 +257,6 @@ class MyForm(settingsmixin.SMainWindow):
'customContextMenuRequested(const QPoint&)'),
self.on_context_menuYourIdentities)
- # load all gui.menu plugins with prefix 'address'
- self.menu_plugins = {'address': []}
- for plugin in get_plugins('gui.menu', 'address'):
- try:
- handler, title = plugin(self)
- except TypeError:
- continue
- self.menu_plugins['address'].append(
- self.ui.addressContextMenuToolbarYourIdentities.addAction(
- title, handler
- ))
-
def init_chan_popup_menu(self, connectSignal=True):
# Popup menu for the Channels tab
self.ui.addressContextMenuToolbar = QtGui.QToolBar()
@@ -1739,8 +1729,6 @@ class MyForm(settingsmixin.SMainWindow):
sent.item(i, 3).setText(textToDisplay)
def updateSentItemStatusByAckdata(self, ackdata, textToDisplay):
- if type(ackdata) is str:
- ackdata = QtCore.QByteArray(ackdata)
for sent in [self.ui.tableWidgetInbox, self.ui.tableWidgetInboxSubscriptions, self.ui.tableWidgetInboxChans]:
treeWidget = self.widgetConvert(sent)
if self.getCurrentFolder(treeWidget) != "sent":
@@ -2244,7 +2232,7 @@ class MyForm(settingsmixin.SMainWindow):
treeWidget = self.widgetConvert(sent)
if self.getCurrentFolder(treeWidget) != "sent":
continue
- if treeWidget == self.ui.treeWidgetYourIdentities and self.getCurrentAccount(treeWidget) not in (fromAddress, None, False):
+ if treeWidget == self.ui.treeWidgetYourIdentities and self.getCurrentAccount(treeWidget) != fromAddress:
continue
elif treeWidget in [self.ui.treeWidgetSubscriptions, self.ui.treeWidgetChans] and self.getCurrentAccount(treeWidget) != toAddress:
continue
@@ -2252,7 +2240,7 @@ class MyForm(settingsmixin.SMainWindow):
continue
self.addMessageListItemSent(sent, toAddress, fromAddress, subject, "msgqueued", ackdata, time.time())
- self.getAccountTextedit(acct).setPlainText(unicode(message, 'utf-8', 'replace'))
+ self.getAccountTextedit(acct).setPlainText(unicode(message, 'utf-8)', 'replace'))
sent.setCurrentCell(0, 0)
def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message):
@@ -2711,10 +2699,10 @@ class MyForm(settingsmixin.SMainWindow):
waitForSync = False
# C PoW currently doesn't support interrupting and OpenCL is untested
- if getPowType() == "python" and (powQueueSize() > 0 or pendingUpload() > 0):
+ if getPowType() == "python" and (powQueueSize() > 0 or PendingUpload().len() > 0):
reply = QtGui.QMessageBox.question(self, _translate("MainWindow", "Proof of work pending"),
_translate("MainWindow", "%n object(s) pending proof of work", None, QtCore.QCoreApplication.CodecForTr, powQueueSize()) + ", " +
- _translate("MainWindow", "%n object(s) waiting to be distributed", None, QtCore.QCoreApplication.CodecForTr, pendingUpload()) + "\n\n" +
+ _translate("MainWindow", "%n object(s) waiting to be distributed", None, QtCore.QCoreApplication.CodecForTr, PendingUpload().len()) + "\n\n" +
_translate("MainWindow", "Wait until these tasks finish?"),
QtGui.QMessageBox.Yes|QtGui.QMessageBox.No|QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel)
if reply == QtGui.QMessageBox.No:
@@ -2722,14 +2710,16 @@ class MyForm(settingsmixin.SMainWindow):
elif reply == QtGui.QMessageBox.Cancel:
return
- if pendingDownload() > 0:
+ if PendingDownloadQueue.totalSize() > 0:
reply = QtGui.QMessageBox.question(self, _translate("MainWindow", "Synchronisation pending"),
- _translate("MainWindow", "Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes?", None, QtCore.QCoreApplication.CodecForTr, pendingDownload()),
+ _translate("MainWindow", "Bitmessage hasn't synchronised with the network, %n object(s) to be downloaded. If you quit now, it may cause delivery delays. Wait until the synchronisation finishes?", None, QtCore.QCoreApplication.CodecForTr, PendingDownloadQueue.totalSize()),
QtGui.QMessageBox.Yes|QtGui.QMessageBox.No|QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Cancel)
if reply == QtGui.QMessageBox.Yes:
waitForSync = True
elif reply == QtGui.QMessageBox.Cancel:
return
+ else:
+ PendingDownloadQueue.stop()
if shared.statusIconColor == 'red' and not BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'dontconnect'):
@@ -2760,7 +2750,7 @@ class MyForm(settingsmixin.SMainWindow):
if waitForSync:
self.updateStatusBar(_translate(
"MainWindow", "Waiting for finishing synchronisation..."))
- while pendingDownload() > 0:
+ while PendingDownloadQueue.totalSize() > 0:
time.sleep(0.5)
QtCore.QCoreApplication.processEvents(
QtCore.QEventLoop.AllEvents, 1000
@@ -2802,18 +2792,19 @@ class MyForm(settingsmixin.SMainWindow):
# check if upload (of objects created locally) pending
self.updateStatusBar(_translate(
"MainWindow", "Waiting for objects to be sent... %1%").arg(50))
- maxPendingUpload = max(1, pendingUpload())
-
- while pendingUpload() > 1:
- self.updateStatusBar(_translate(
- "MainWindow",
- "Waiting for objects to be sent... %1%"
- ).arg(int(50 + 20 * (pendingUpload()/maxPendingUpload)))
- )
- time.sleep(0.5)
- QtCore.QCoreApplication.processEvents(
- QtCore.QEventLoop.AllEvents, 1000
- )
+ try:
+ while PendingUpload().progress() < 1:
+ self.updateStatusBar(_translate(
+ "MainWindow",
+ "Waiting for objects to be sent... %1%"
+ ).arg(int(50 + 20 * PendingUpload().progress()))
+ )
+ time.sleep(0.5)
+ QtCore.QCoreApplication.processEvents(
+ QtCore.QEventLoop.AllEvents, 1000
+ )
+ except PendingUploadDeadlineException:
+ pass
QtCore.QCoreApplication.processEvents(
QtCore.QEventLoop.AllEvents, 1000
@@ -3433,10 +3424,6 @@ class MyForm(settingsmixin.SMainWindow):
self.popMenuSubscriptions.addSeparator()
self.popMenuSubscriptions.addAction(self.actionsubscriptionsClipboard)
self.popMenuSubscriptions.addSeparator()
- # preloaded gui.menu plugins with prefix 'address'
- for plugin in self.menu_plugins['address']:
- self.popMenuSubscriptions.addAction(plugin)
- self.popMenuSubscriptions.addSeparator()
self.popMenuSubscriptions.addAction(self.actionMarkAllRead)
self.popMenuSubscriptions.exec_(
self.ui.treeWidgetSubscriptions.mapToGlobal(point))
@@ -3847,13 +3834,13 @@ class MyForm(settingsmixin.SMainWindow):
self.popMenuYourIdentities.addAction(self.actionSpecialAddressBehaviorYourIdentities)
self.popMenuYourIdentities.addAction(self.actionEmailGateway)
self.popMenuYourIdentities.addSeparator()
- if currentItem.type != AccountMixin.ALL:
- # preloaded gui.menu plugins with prefix 'address'
- for plugin in self.menu_plugins['address']:
- self.popMenuYourIdentities.addAction(plugin)
- self.popMenuYourIdentities.addSeparator()
self.popMenuYourIdentities.addAction(self.actionMarkAllRead)
+ if get_plugins:
+ for plugin in get_plugins(
+ 'gui.menu', 'popMenuYourIdentities'):
+ plugin(self)
+
self.popMenuYourIdentities.exec_(
self.ui.treeWidgetYourIdentities.mapToGlobal(point))
@@ -3873,10 +3860,6 @@ class MyForm(settingsmixin.SMainWindow):
self.popMenu.addAction(self.actionEnable)
self.popMenu.addAction(self.actionSetAvatar)
self.popMenu.addSeparator()
- # preloaded gui.menu plugins with prefix 'address'
- for plugin in self.menu_plugins['address']:
- self.popMenu.addAction(plugin)
- self.popMenu.addSeparator()
self.popMenu.addAction(self.actionMarkAllRead)
self.popMenu.exec_(
self.ui.treeWidgetChans.mapToGlobal(point))
diff --git a/src/bitmessageqt/blacklist.py b/src/bitmessageqt/blacklist.py
index 64413ebb..e07f9469 100644
--- a/src/bitmessageqt/blacklist.py
+++ b/src/bitmessageqt/blacklist.py
@@ -56,10 +56,9 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
def click_pushButtonAddBlacklist(self):
self.NewBlacklistDialogInstance = AddAddressDialog(self)
if self.NewBlacklistDialogInstance.exec_():
- if self.NewBlacklistDialogInstance.labelAddressCheck.text() == \
- _translate("MainWindow", "Address is valid."):
+ if self.NewBlacklistDialogInstance.ui.labelAddressCheck.text() == _translate("MainWindow", "Address is valid."):
address = addBMIfNotPresent(str(
- self.NewBlacklistDialogInstance.lineEditAddress.text()))
+ self.NewBlacklistDialogInstance.ui.lineEditAddress.text()))
# First we must check to see if the address is already in the
# address book. The user cannot add it again or else it will
# cause problems when updating and deleting the entry.
@@ -73,7 +72,7 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
self.tableWidgetBlacklist.setSortingEnabled(False)
self.tableWidgetBlacklist.insertRow(0)
newItem = QtGui.QTableWidgetItem(unicode(
- self.NewBlacklistDialogInstance.lineEditLabel.text().toUtf8(), 'utf-8'))
+ self.NewBlacklistDialogInstance.ui.newAddressLabel.text().toUtf8(), 'utf-8'))
newItem.setIcon(avatarize(address))
self.tableWidgetBlacklist.setItem(0, 0, newItem)
newItem = QtGui.QTableWidgetItem(address)
@@ -81,7 +80,7 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.tableWidgetBlacklist.setItem(0, 1, newItem)
self.tableWidgetBlacklist.setSortingEnabled(True)
- t = (str(self.NewBlacklistDialogInstance.lineEditLabel.text().toUtf8()), address, True)
+ t = (str(self.NewBlacklistDialogInstance.ui.newAddressLabel.text().toUtf8()), address, True)
if BMConfigParser().get('bitmessagesettings', 'blackwhitelist') == 'black':
sql = '''INSERT INTO blacklist VALUES (?,?,?)'''
else:
diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py
index 3691d5b3..06b1e0ce 100644
--- a/src/bitmessageqt/networkstatus.py
+++ b/src/bitmessageqt/networkstatus.py
@@ -3,7 +3,7 @@ import time
import shared
from tr import _translate
-from inventory import Inventory
+from inventory import Inventory, PendingDownloadQueue, PendingUpload
import knownnodes
import l10n
import network.stats
diff --git a/src/class_objectHashHolder.py b/src/class_objectHashHolder.py
new file mode 100644
index 00000000..2e456d8c
--- /dev/null
+++ b/src/class_objectHashHolder.py
@@ -0,0 +1,54 @@
+# objectHashHolder is a timer-driven thread. One objectHashHolder thread is used
+# by each sendDataThread. The sendDataThread uses it whenever it needs to
+# advertise an object to peers in an inv message, or advertise a peer to other
+# peers in an addr message. Instead of sending them out immediately, it must
+# wait a random number of seconds for each connection so that different peers
+# get different objects at different times. Thus an attacker who is
+# connecting to many network nodes who receives a message first from Alice
+# cannot be sure if Alice is the node who originated the message.
+
+import random
+import time
+import threading
+
+class objectHashHolder(threading.Thread):
+ size = 10
+ def __init__(self, sendDataThreadMailbox):
+ threading.Thread.__init__(self, name="objectHashHolder")
+ self.shutdown = False
+ self.sendDataThreadMailbox = sendDataThreadMailbox # This queue is used to submit data back to our associated sendDataThread.
+ self.collectionOfHashLists = []
+ self.collectionOfPeerLists = []
+ for i in range(objectHashHolder.size):
+ self.collectionOfHashLists.append([])
+ self.collectionOfPeerLists.append([])
+
+ def run(self):
+ iterator = 0
+ while not self.shutdown:
+ if len(self.collectionOfHashLists[iterator]) > 0:
+ self.sendDataThreadMailbox.put((0, 'sendinv', self.collectionOfHashLists[iterator]))
+ self.collectionOfHashLists[iterator] = []
+ if len(self.collectionOfPeerLists[iterator]) > 0:
+ self.sendDataThreadMailbox.put((0, 'sendaddr', self.collectionOfPeerLists[iterator]))
+ self.collectionOfPeerLists[iterator] = []
+ iterator += 1
+ iterator %= objectHashHolder.size
+ time.sleep(1)
+
+ def holdHash(self,hash):
+ self.collectionOfHashLists[random.randrange(0, objectHashHolder.size)].append(hash)
+
+ def hasHash(self, hash):
+ if hash in (hashlist for hashlist in self.collectionOfHashLists):
+ return True
+ return False
+
+ def holdPeer(self,peerDetails):
+ self.collectionOfPeerLists[random.randrange(0, objectHashHolder.size)].append(peerDetails)
+
+ def hashCount(self):
+ return sum([len(x) for x in self.collectionOfHashLists if type(x) is list])
+
+ def close(self):
+ self.shutdown = True
diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py
new file mode 100644
index 00000000..9b3eac14
--- /dev/null
+++ b/src/class_outgoingSynSender.py
@@ -0,0 +1,283 @@
+import errno
+import threading
+import time
+import random
+import shared
+import select
+import socks
+import socket
+import sys
+import tr
+
+from class_sendDataThread import *
+from class_receiveDataThread import *
+from bmconfigparser import BMConfigParser
+from helper_threading import *
+import knownnodes
+import queues
+import state
+
+# For each stream to which we connect, several outgoingSynSender threads
+# will exist and will collectively create 8 connections with peers.
+
+class outgoingSynSender(threading.Thread, StoppableThread):
+
+ def __init__(self):
+ threading.Thread.__init__(self, name="outgoingSynSender")
+ self.initStop()
+ random.seed()
+
+ def setup(self, streamNumber, selfInitiatedConnections):
+ self.streamNumber = streamNumber
+ self.selfInitiatedConnections = selfInitiatedConnections
+
+ def _getPeer(self):
+ # If the user has specified a trusted peer then we'll only
+ # ever connect to that. Otherwise we'll pick a random one from
+ # the known nodes
+ if state.trustedPeer:
+ with knownnodes.knownNodesLock:
+ peer = state.trustedPeer
+ knownnodes.knownNodes[self.streamNumber][peer] = time.time()
+ else:
+ while not self._stopped:
+ try:
+ with knownnodes.knownNodesLock:
+ peer, = random.sample(knownnodes.knownNodes[self.streamNumber], 1)
+ priority = (183600 - (time.time() - knownnodes.knownNodes[self.streamNumber][peer])) / 183600 # 2 days and 3 hours
+ except ValueError: # no known nodes
+ self.stop.wait(1)
+ continue
+ if BMConfigParser().get('bitmessagesettings', 'socksproxytype') != 'none':
+ if peer.host.find(".onion") == -1:
+ priority /= 10 # hidden services have 10x priority over plain net
+ else:
+ # don't connect to self
+ if peer.host == BMConfigParser().get('bitmessagesettings', 'onionhostname') and peer.port == BMConfigParser().getint("bitmessagesettings", "onionport"):
+ continue
+ elif peer.host.find(".onion") != -1: # onion address and so proxy
+ continue
+ if priority <= 0.001: # everyone has at least this much priority
+ priority = 0.001
+ if (random.random() <= priority):
+ break
+ self.stop.wait(0.01) # prevent CPU hogging if something is broken
+ try:
+ return peer
+ except NameError:
+ return state.Peer('127.0.0.1', 8444)
+
+ def stopThread(self):
+ super(outgoingSynSender, self).stopThread()
+ try:
+ self.sock.shutdown(socket.SHUT_RDWR)
+ except:
+ pass
+
+ def run(self):
+ while BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect') and not self._stopped:
+ self.stop.wait(2)
+ while BMConfigParser().safeGetBoolean('bitmessagesettings', 'sendoutgoingconnections') and not self._stopped:
+ self.name = "outgoingSynSender"
+ maximumConnections = 1 if state.trustedPeer else BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections')
+ while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections and not self._stopped:
+ self.stop.wait(10)
+ if state.shutdown:
+ break
+ peer = self._getPeer()
+ while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
+ # print 'choosing new sample'
+ peer = self._getPeer()
+ self.stop.wait(1)
+ if self._stopped:
+ break
+ # Clear out the shared.alreadyAttemptedConnectionsList every half
+ # hour so that this program will again attempt a connection
+ # to any nodes, even ones it has already tried.
+ with shared.alreadyAttemptedConnectionsListLock:
+ if (time.time() - shared.alreadyAttemptedConnectionsListResetTime) > 1800:
+ shared.alreadyAttemptedConnectionsList.clear()
+ shared.alreadyAttemptedConnectionsListResetTime = int(
+ time.time())
+ shared.alreadyAttemptedConnectionsList[peer] = 0
+ if self._stopped:
+ break
+ self.name = "outgoingSynSender-" + peer.host.replace(":", ".") # log parser field separator
+ address_family = socket.AF_INET
+ # Proxy IP is IPv6. Unlikely but possible
+ if BMConfigParser().get('bitmessagesettings', 'socksproxytype') != 'none':
+ if ":" in BMConfigParser().get('bitmessagesettings', 'sockshostname'):
+ address_family = socket.AF_INET6
+ # No proxy, and destination is IPv6
+ elif peer.host.find(':') >= 0 :
+ address_family = socket.AF_INET6
+ try:
+ self.sock = socks.socksocket(address_family, socket.SOCK_STREAM)
+ except:
+ """
+ The line can fail on Windows systems which aren't
+ 64-bit compatiable:
+ File "C:\Python27\lib\socket.py", line 187, in __init__
+ _sock = _realsocket(family, type, proto)
+ error: [Errno 10047] An address incompatible with the requested protocol was used
+
+ So let us remove the offending address from our knownNodes file.
+ """
+ with knownnodes.knownNodesLock:
+ try:
+ del knownnodes.knownNodes[self.streamNumber][peer]
+ except KeyError:
+ pass
+ logger.debug('deleting ' + str(peer) + ' from knownnodes.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible.')
+ continue
+ # This option apparently avoids the TIME_WAIT state so that we
+ # can rebind faster
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.sock.settimeout(20)
+ if BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2:
+ logger.debug('Trying an outgoing connection to ' + str(peer))
+
+ # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ elif BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'SOCKS4a':
+ if shared.verbose >= 2:
+ logger.debug ('(Using SOCKS4a) Trying an outgoing connection to ' + str(peer))
+
+ proxytype = socks.PROXY_TYPE_SOCKS4
+ sockshostname = BMConfigParser().get(
+ 'bitmessagesettings', 'sockshostname')
+ socksport = BMConfigParser().getint(
+ 'bitmessagesettings', 'socksport')
+ rdns = True # 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.
+ if BMConfigParser().getboolean('bitmessagesettings', 'socksauthentication'):
+ socksusername = BMConfigParser().get(
+ 'bitmessagesettings', 'socksusername')
+ sockspassword = BMConfigParser().get(
+ 'bitmessagesettings', 'sockspassword')
+ self.sock.setproxy(
+ proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
+ else:
+ self.sock.setproxy(
+ proxytype, sockshostname, socksport, rdns)
+ elif BMConfigParser().get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
+ if shared.verbose >= 2:
+ logger.debug ('(Using SOCKS5) Trying an outgoing connection to ' + str(peer))
+
+ proxytype = socks.PROXY_TYPE_SOCKS5
+ sockshostname = BMConfigParser().get(
+ 'bitmessagesettings', 'sockshostname')
+ socksport = BMConfigParser().getint(
+ 'bitmessagesettings', 'socksport')
+ rdns = True # 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.
+ if BMConfigParser().getboolean('bitmessagesettings', 'socksauthentication'):
+ socksusername = BMConfigParser().get(
+ 'bitmessagesettings', 'socksusername')
+ sockspassword = BMConfigParser().get(
+ 'bitmessagesettings', 'sockspassword')
+ self.sock.setproxy(
+ proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
+ else:
+ self.sock.setproxy(
+ proxytype, sockshostname, socksport, rdns)
+
+ try:
+ self.sock.connect((peer.host, peer.port))
+ if self._stopped:
+ self.sock.shutdown(socket.SHUT_RDWR)
+ self.sock.close()
+ return
+ sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection.
+
+ sd = sendDataThread(sendDataThreadQueue)
+ sd.setup(self.sock, peer.host, peer.port, self.streamNumber)
+ sd.start()
+
+ rd = receiveDataThread()
+ rd.daemon = True # close the main program even if there are threads left
+ rd.setup(self.sock,
+ peer.host,
+ peer.port,
+ self.streamNumber,
+ self.selfInitiatedConnections,
+ sendDataThreadQueue,
+ sd.objectHashHolderInstance)
+ rd.start()
+
+ sd.sendVersionMessage()
+
+ logger.debug(str(self) + ' connected to ' + str(peer) + ' during an outgoing attempt.')
+ except socks.GeneralProxyError as err:
+ if err[0][0] in [7, 8, 9]:
+ logger.error('Error communicating with proxy: %s', str(err))
+ queues.UISignalQueue.put((
+ 'updateStatusBar',
+ tr._translate(
+ "MainWindow", "Problem communicating with proxy: %1. Please check your network settings.").arg(str(err[0][1]))
+ ))
+ self.stop.wait(1)
+ continue
+ elif shared.verbose >= 2:
+ logger.debug('Could NOT connect to ' + str(peer) + ' during outgoing attempt. ' + str(err))
+
+ deletedPeer = None
+ with knownnodes.knownNodesLock:
+ """
+ It is remotely possible that peer is no longer in knownnodes.knownNodes.
+ This could happen if two outgoingSynSender threads both try to
+ connect to the same peer, both fail, and then both try to remove
+ it from knownnodes.knownNodes. This is unlikely because of the
+ alreadyAttemptedConnectionsList but because we clear that list once
+ every half hour, it can happen.
+ """
+ if peer in knownnodes.knownNodes[self.streamNumber]:
+ timeLastSeen = knownnodes.knownNodes[self.streamNumber][peer]
+ if (int(time.time()) - timeLastSeen) > 172800 and len(knownnodes.knownNodes[self.streamNumber]) > 1000: # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the knownnodes.knownNodes data-structure.
+ del knownnodes.knownNodes[self.streamNumber][peer]
+ deletedPeer = peer
+ if deletedPeer:
+ str ('deleting ' + str(peer) + ' from knownnodes.knownNodes because it is more than 48 hours old and we could not connect to it.')
+
+ except socks.Socks5AuthError as err:
+ queues.UISignalQueue.put((
+ 'updateStatusBar', tr._translate(
+ "MainWindow", "SOCKS5 Authentication problem: %1. Please check your SOCKS5 settings.").arg(str(err))))
+ except socks.Socks5Error as err:
+ if err[0][0] in [3, 4, 5, 6]:
+ # this is a more bening "error": host unreachable, network unreachable, connection refused, TTL expired
+ logger.debug('SOCKS5 error: %s', str(err))
+ else:
+ logger.error('SOCKS5 error: %s', str(err))
+ if err[0][0] == 4 or err[0][0] == 2:
+ state.networkProtocolAvailability[protocol.networkType(peer.host)] = False
+ except socks.Socks4Error as err:
+ logger.error('Socks4Error: ' + str(err))
+ except socket.error as err:
+ if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS':
+ logger.error('Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str(err))
+ else:
+ if err[0] == errno.ENETUNREACH:
+ state.networkProtocolAvailability[protocol.networkType(peer.host)] = False
+ if shared.verbose >= 1:
+ logger.debug('Could NOT connect to ' + str(peer) + 'during outgoing attempt. ' + str(err))
+
+ deletedPeer = None
+ with knownnodes.knownNodesLock:
+ """
+ It is remotely possible that peer is no longer in knownnodes.knownNodes.
+ This could happen if two outgoingSynSender threads both try to
+ connect to the same peer, both fail, and then both try to remove
+ it from knownnodes.knownNodes. This is unlikely because of the
+ alreadyAttemptedConnectionsList but because we clear that list once
+ every half hour, it can happen.
+ """
+ if peer in knownnodes.knownNodes[self.streamNumber]:
+ timeLastSeen = knownnodes.knownNodes[self.streamNumber][peer]
+ if (int(time.time()) - timeLastSeen) > 172800 and len(knownnodes.knownNodes[self.streamNumber]) > 1000: # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the knownnodes.knownNodes data-structure.
+ del knownnodes.knownNodes[self.streamNumber][peer]
+ deletedPeer = peer
+ if deletedPeer:
+ logger.debug('deleting ' + str(peer) + ' from knownnodes.knownNodes because it is more than 48 hours old and we could not connect to it.')
+
+ except Exception as err:
+ import traceback
+ logger.exception('An exception has occurred in the outgoingSynSender thread that was not caught by other exception types:')
+ self.stop.wait(0.1)
diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py
new file mode 100644
index 00000000..4e86196c
--- /dev/null
+++ b/src/class_receiveDataThread.py
@@ -0,0 +1,879 @@
+doTimingAttackMitigation = False
+
+import base64
+import datetime
+import errno
+import math
+import time
+import threading
+import shared
+import hashlib
+import os
+import Queue
+import select
+import socket
+import random
+import ssl
+from struct import unpack, pack
+import sys
+import traceback
+from binascii import hexlify
+#import string
+#from subprocess import call # used when the API must execute an outside program
+#from pyelliptic.openssl import OpenSSL
+
+#import highlevelcrypto
+from addresses import *
+from bmconfigparser import BMConfigParser
+from class_objectHashHolder import objectHashHolder
+from helper_generic import addDataPadding, isHostInPrivateIPRange
+from helper_sql import sqlQuery
+import knownnodes
+from debug import logger
+import paths
+import protocol
+from inventory import Inventory, PendingDownloadQueue, PendingUpload
+import queues
+import state
+import throttle
+import tr
+from version import softwareVersion
+
+# This thread is created either by the synSenderThread(for outgoing
+# connections) or the singleListenerThread(for incoming connections).
+
+class receiveDataThread(threading.Thread):
+
+ def __init__(self):
+ threading.Thread.__init__(self, name="receiveData")
+ self.data = ''
+ self.verackSent = False
+ self.verackReceived = False
+
+ def setup(
+ self,
+ sock,
+ HOST,
+ port,
+ streamNumber,
+ selfInitiatedConnections,
+ sendDataThreadQueue,
+ objectHashHolderInstance):
+
+ self.sock = sock
+ self.peer = state.Peer(HOST, port)
+ self.name = "receiveData-" + self.peer.host.replace(":", ".") # ":" log parser field separator
+ self.streamNumber = state.streamsInWhichIAmParticipating
+ self.remoteStreams = []
+ self.selfInitiatedConnections = selfInitiatedConnections
+ self.sendDataThreadQueue = sendDataThreadQueue # used to send commands and data to the sendDataThread
+ self.hostIdent = self.peer.port if ".onion" in BMConfigParser().get('bitmessagesettings', 'onionhostname') and protocol.checkSocksIP(self.peer.host) else self.peer.host
+ shared.connectedHostsList[
+ self.hostIdent] = 0 # The very fact that this receiveData thread exists shows that we are connected to the remote host. Let's add it to this list so that an outgoingSynSender thread doesn't try to connect to it.
+ self.connectionIsOrWasFullyEstablished = False # set to true after the remote node and I accept each other's version messages. This is needed to allow the user interface to accurately reflect the current number of connections.
+ self.services = 0
+ if streamNumber == -1: # This was an incoming connection. Send out a version message if we accept the other node's version message.
+ self.initiatedConnection = False
+ else:
+ self.initiatedConnection = True
+ for stream in self.streamNumber:
+ self.selfInitiatedConnections[stream][self] = 0
+ self.objectHashHolderInstance = objectHashHolderInstance
+ self.downloadQueue = PendingDownloadQueue()
+ self.startTime = time.time()
+
+ def run(self):
+ logger.debug('receiveDataThread starting. ID ' + str(id(self)) + '. The size of the shared.connectedHostsList is now ' + str(len(shared.connectedHostsList)))
+
+ while state.shutdown == 0:
+ dataLen = len(self.data)
+ try:
+ isSSL = False
+ if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and
+ self.connectionIsOrWasFullyEstablished and
+ protocol.haveSSL(not self.initiatedConnection)):
+ isSSL = True
+ dataRecv = self.sslSock.recv(throttle.ReceiveThrottle().chunkSize)
+ else:
+ dataRecv = self.sock.recv(throttle.ReceiveThrottle().chunkSize)
+ self.data += dataRecv
+ throttle.ReceiveThrottle().wait(len(dataRecv))
+ except socket.timeout:
+ if self.connectionIsOrWasFullyEstablished:
+ self.sendping("Still around!")
+ continue
+ logger.error("Timeout during protocol initialisation")
+ break
+ except ssl.SSLError as err:
+ if err.errno == ssl.SSL_ERROR_WANT_READ:
+ select.select([self.sslSock], [], [], 10)
+ logger.debug('sock.recv retriable SSL error')
+ continue
+ if err.errno is None and 'timed out' in str(err):
+ if self.connectionIsOrWasFullyEstablished:
+ self.sendping("Still around!")
+ continue
+ logger.error ('SSL error: %i/%s', err.errno if err.errno else 0, str(err))
+ break
+ except socket.error as err:
+ if err.errno in (errno.EAGAIN, errno.EWOULDBLOCK) or \
+ (sys.platform.startswith('win') and \
+ err.errno == errno.WSAEWOULDBLOCK):
+ select.select([self.sslSock if isSSL else self.sock], [], [], 10)
+ logger.debug('sock.recv retriable error')
+ continue
+ logger.error('sock.recv error. Closing receiveData thread, %s', str(err))
+ break
+ # print 'Received', repr(self.data)
+ if len(self.data) == dataLen: # If self.sock.recv returned no data:
+ logger.debug('Connection to ' + str(self.peer) + ' closed. Closing receiveData thread')
+ break
+ else:
+ self.processData()
+
+ try:
+ for stream in self.streamNumber:
+ try:
+ del self.selfInitiatedConnections[stream][self]
+ except KeyError:
+ pass
+ logger.debug('removed self (a receiveDataThread) from selfInitiatedConnections')
+ except:
+ pass
+ self.sendDataThreadQueue.put((0, 'shutdown','no data')) # commands the corresponding sendDataThread to shut itself down.
+ try:
+ del shared.connectedHostsList[self.hostIdent]
+ except Exception as err:
+ logger.error('Could not delete ' + str(self.hostIdent) + ' from shared.connectedHostsList.' + str(err))
+
+ queues.UISignalQueue.put(('updateNetworkStatusTab', 'no data'))
+ self.checkTimeOffsetNotification()
+ logger.debug('receiveDataThread ending. ID ' + str(id(self)) + '. The size of the shared.connectedHostsList is now ' + str(len(shared.connectedHostsList)))
+
+ def antiIntersectionDelay(self, initial = False):
+ # estimated time for a small object to propagate across the whole network
+ delay = math.ceil(math.log(max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) + 2, 20)) * (0.2 + objectHashHolder.size/2)
+ # take the stream with maximum amount of nodes
+ # +2 is to avoid problems with log(0) and log(1)
+ # 20 is avg connected nodes count
+ # 0.2 is avg message transmission time
+ now = time.time()
+ if initial and now - delay < self.startTime:
+ logger.debug("Initial sleeping for %.2fs", delay - (now - self.startTime))
+ time.sleep(delay - (now - self.startTime))
+ elif not initial:
+ logger.debug("Sleeping due to missing object for %.2fs", delay)
+ time.sleep(delay)
+
+ def checkTimeOffsetNotification(self):
+ if shared.timeOffsetWrongCount >= 4 and not self.connectionIsOrWasFullyEstablished:
+ queues.UISignalQueue.put(('updateStatusBar', tr._translate("MainWindow", "The time on your computer, %1, may be wrong. Please verify your settings.").arg(datetime.datetime.now().strftime("%H:%M:%S"))))
+
+ def processData(self):
+ if len(self.data) < protocol.Header.size: # if so little of the data has arrived that we can't even read the checksum then wait for more data.
+ return
+
+ magic,command,payloadLength,checksum = protocol.Header.unpack(self.data[:protocol.Header.size])
+ if magic != 0xE9BEB4D9:
+ self.data = ""
+ return
+ if payloadLength > 1600100: # ~1.6 MB which is the maximum possible size of an inv message.
+ logger.info('The incoming message, which we have not yet download, is too large. Ignoring it. (unfortunately there is no way to tell the other node to stop sending it except to disconnect.) Message size: %s' % payloadLength)
+ self.data = self.data[payloadLength + protocol.Header.size:]
+ del magic,command,payloadLength,checksum # we don't need these anymore and better to clean them now before the recursive call rather than after
+ self.processData()
+ return
+ if len(self.data) < payloadLength + protocol.Header.size: # check if the whole message has arrived yet.
+ return
+ payload = self.data[protocol.Header.size:payloadLength + protocol.Header.size]
+ if checksum != hashlib.sha512(payload).digest()[0:4]: # test the checksum in the message.
+ logger.error('Checksum incorrect. Clearing this message.')
+ self.data = self.data[payloadLength + protocol.Header.size:]
+ del magic,command,payloadLength,checksum,payload # better to clean up before the recursive call
+ self.processData()
+ return
+
+ # The time we've last seen this node is obviously right now since we
+ # just received valid data from it. So update the knownNodes list so
+ # that other peers can be made aware of its existance.
+ if self.initiatedConnection and self.connectionIsOrWasFullyEstablished: # The remote port is only something we should share with others if it is the remote node's incoming port (rather than some random operating-system-assigned outgoing port).
+ with knownnodes.knownNodesLock:
+ for stream in self.streamNumber:
+ knownnodes.knownNodes[stream][self.peer] = int(time.time())
+
+ #Strip the nulls
+ command = command.rstrip('\x00')
+ logger.debug('remoteCommand ' + repr(command) + ' from ' + str(self.peer))
+
+ try:
+ #TODO: Use a dispatcher here
+ if command == 'error':
+ self.recerror(payload)
+ elif not self.connectionIsOrWasFullyEstablished:
+ if command == 'version':
+ self.recversion(payload)
+ elif command == 'verack':
+ self.recverack()
+ else:
+ if command == 'addr':
+ self.recaddr(payload)
+ elif command == 'inv':
+ self.recinv(payload)
+ elif command == 'getdata':
+ self.recgetdata(payload)
+ elif command == 'object':
+ self.recobject(payload)
+ elif command == 'ping':
+ self.sendpong(payload)
+ elif command == 'pong':
+ pass
+ else:
+ logger.info("Unknown command %s, ignoring", command)
+ except varintDecodeError as e:
+ logger.debug("There was a problem with a varint while processing a message from the wire. Some details: %s" % e)
+ except Exception as e:
+ logger.critical("Critical error in a receiveDataThread: \n%s" % traceback.format_exc())
+
+ del payload
+ self.data = self.data[payloadLength + protocol.Header.size:] # take this message out and then process the next message
+
+ if self.data == '': # if there are no more messages
+ toRequest = []
+ try:
+ for i in range(len(self.downloadQueue.pending), 100):
+ while True:
+ hashId = self.downloadQueue.get(False)
+ if not hashId in Inventory():
+ toRequest.append(hashId)
+ break
+ # don't track download for duplicates
+ self.downloadQueue.task_done(hashId)
+ except Queue.Empty:
+ pass
+ if len(toRequest) > 0:
+ self.sendgetdata(toRequest)
+ self.processData()
+
+ def sendpong(self, payload):
+ logger.debug('Sending pong')
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.CreatePacket('pong', payload)))
+
+ def sendping(self, payload):
+ logger.debug('Sending ping')
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.CreatePacket('ping', payload)))
+
+ def recverack(self):
+ logger.debug('verack received')
+ self.verackReceived = True
+ if self.verackSent:
+ # We have thus both sent and received a verack.
+ self.connectionFullyEstablished()
+
+ def sslHandshake(self):
+ self.sslSock = self.sock
+ if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and
+ protocol.haveSSL(not self.initiatedConnection)):
+ logger.debug("Initialising TLS")
+ if sys.version_info >= (2,7,9):
+ context = ssl.SSLContext(protocol.sslProtocolVersion)
+ context.set_ciphers(protocol.sslProtocolCiphers)
+ context.set_ecdh_curve("secp256k1")
+ context.check_hostname = False
+ context.verify_mode = ssl.CERT_NONE
+ # also exclude TLSv1 and TLSv1.1 in the future
+ context.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE | ssl.OP_CIPHER_SERVER_PREFERENCE
+ self.sslSock = context.wrap_socket(self.sock, server_side = not self.initiatedConnection, do_handshake_on_connect=False)
+ else:
+ self.sslSock = ssl.wrap_socket(self.sock, keyfile = os.path.join(paths.codePath(), 'sslkeys', 'key.pem'), certfile = os.path.join(paths.codePath(), 'sslkeys', 'cert.pem'), server_side = not self.initiatedConnection, ssl_version=protocol.sslProtocolVersion, do_handshake_on_connect=False, ciphers=protocol.sslProtocolCiphers)
+ self.sendDataThreadQueue.join()
+ while True:
+ try:
+ self.sslSock.do_handshake()
+ logger.debug("TLS handshake success")
+ if sys.version_info >= (2, 7, 9):
+ logger.debug("TLS protocol version: %s", self.sslSock.version())
+ break
+ except ssl.SSLError as e:
+ if sys.hexversion >= 0x02070900:
+ if isinstance (e, ssl.SSLWantReadError):
+ logger.debug("Waiting for SSL socket handhake read")
+ select.select([self.sslSock], [], [], 10)
+ continue
+ elif isinstance (e, ssl.SSLWantWriteError):
+ logger.debug("Waiting for SSL socket handhake write")
+ select.select([], [self.sslSock], [], 10)
+ continue
+ else:
+ if e.args[0] == ssl.SSL_ERROR_WANT_READ:
+ logger.debug("Waiting for SSL socket handhake read")
+ select.select([self.sslSock], [], [], 10)
+ continue
+ elif e.args[0] == ssl.SSL_ERROR_WANT_WRITE:
+ logger.debug("Waiting for SSL socket handhake write")
+ select.select([], [self.sslSock], [], 10)
+ continue
+ logger.error("SSL socket handhake failed: shutting down connection, %s", str(e))
+ self.sendDataThreadQueue.put((0, 'shutdown','tls handshake fail %s' % (str(e))))
+ return False
+ except socket.error as err:
+ logger.debug('SSL socket handshake failed, shutting down connection, %s', str(err))
+ self.sendDataThreadQueue.put((0, 'shutdown','tls handshake fail'))
+ return False
+ except Exception:
+ logger.error("SSL socket handhake failed, shutting down connection", exc_info=True)
+ self.sendDataThreadQueue.put((0, 'shutdown','tls handshake fail'))
+ return False
+ # SSL in the background should be blocking, otherwise the error handling is difficult
+ self.sslSock.settimeout(None)
+ return True
+ # no SSL
+ return True
+
+ def peerValidityChecks(self):
+ if self.remoteProtocolVersion < 3:
+ self.sendDataThreadQueue.put((0, 'sendRawData',protocol.assembleErrorMessage(
+ fatal=2, errorText="Your is using an old protocol. Closing connection.")))
+ logger.debug ('Closing connection to old protocol version ' + str(self.remoteProtocolVersion) + ' node: ' + str(self.peer))
+ return False
+ if self.timeOffset > 3600:
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.assembleErrorMessage(
+ fatal=2, errorText="Your time is too far in the future compared to mine. Closing connection.")))
+ logger.info("%s's time is too far in the future (%s seconds). Closing connection to it.", self.peer, self.timeOffset)
+ shared.timeOffsetWrongCount += 1
+ time.sleep(2)
+ return False
+ elif self.timeOffset < -3600:
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.assembleErrorMessage(
+ fatal=2, errorText="Your time is too far in the past compared to mine. Closing connection.")))
+ logger.info("%s's time is too far in the past (timeOffset %s seconds). Closing connection to it.", self.peer, self.timeOffset)
+ shared.timeOffsetWrongCount += 1
+ return False
+ else:
+ shared.timeOffsetWrongCount = 0
+ if len(self.streamNumber) == 0:
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.assembleErrorMessage(
+ fatal=2, errorText="We don't have shared stream interests. Closing connection.")))
+ logger.debug ('Closed connection to ' + str(self.peer) + ' because there is no overlapping interest in streams.')
+ return False
+ return True
+
+ def connectionFullyEstablished(self):
+ if self.connectionIsOrWasFullyEstablished:
+ # there is no reason to run this function a second time
+ return
+
+ if not self.sslHandshake():
+ return
+
+ if self.peerValidityChecks() == False:
+ time.sleep(2)
+ self.sendDataThreadQueue.put((0, 'shutdown','no data'))
+ self.checkTimeOffsetNotification()
+ return
+
+ self.connectionIsOrWasFullyEstablished = True
+ shared.timeOffsetWrongCount = 0
+
+ # Command the corresponding sendDataThread to set its own connectionIsOrWasFullyEstablished variable to True also
+ self.sendDataThreadQueue.put((0, 'connectionIsOrWasFullyEstablished', (self.services, self.sslSock)))
+
+ if not self.initiatedConnection:
+ shared.clientHasReceivedIncomingConnections = True
+ queues.UISignalQueue.put(('setStatusIcon', 'green'))
+ self.sock.settimeout(
+ 600) # We'll send out a ping every 5 minutes to make sure the connection stays alive if there has been no other traffic to send lately.
+ queues.UISignalQueue.put(('updateNetworkStatusTab', 'no data'))
+ logger.debug('Connection fully established with ' + str(self.peer) + "\n" + \
+ 'The size of the connectedHostsList is now ' + str(len(shared.connectedHostsList)) + "\n" + \
+ 'The length of sendDataQueues is now: ' + str(len(state.sendDataQueues)) + "\n" + \
+ 'broadcasting addr from within connectionFullyEstablished function.')
+
+ if self.initiatedConnection:
+ state.networkProtocolAvailability[protocol.networkType(self.peer.host)] = True
+
+ # we need to send our own objects to this node
+ PendingUpload().add()
+
+ # Let all of our peers know about this new node.
+ for stream in self.remoteStreams:
+ dataToSend = (int(time.time()), stream, self.services, self.peer.host, self.remoteNodeIncomingPort)
+ protocol.broadcastToSendDataQueues((
+ stream, 'advertisepeer', dataToSend))
+
+ self.sendaddr() # This is one large addr message to this one peer.
+ if len(shared.connectedHostsList) > \
+ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections", 200):
+ logger.info ('We are connected to too many people. Closing connection.')
+ if self.initiatedConnection:
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.assembleErrorMessage(fatal=2, errorText="Thank you for providing a listening node.")))
+ else:
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.assembleErrorMessage(fatal=2, errorText="Server full, please try again later.")))
+ self.sendDataThreadQueue.put((0, 'shutdown','no data'))
+ return
+ self.sendBigInv()
+
+ def sendBigInv(self):
+ # Select all hashes for objects in this stream.
+ bigInvList = {}
+ for stream in self.streamNumber:
+ for hash in Inventory().unexpired_hashes_by_stream(stream):
+ if not self.objectHashHolderInstance.hasHash(hash):
+ bigInvList[hash] = 0
+ numberOfObjectsInInvMessage = 0
+ payload = ''
+ # Now let us start appending all of these hashes together. They will be
+ # sent out in a big inv message to our new peer.
+ for hash, storedValue in bigInvList.items():
+ payload += hash
+ numberOfObjectsInInvMessage += 1
+ if numberOfObjectsInInvMessage == 50000: # We can only send a max of 50000 items per inv message but we may have more objects to advertise. They must be split up into multiple inv messages.
+ self.sendinvMessageToJustThisOnePeer(
+ numberOfObjectsInInvMessage, payload)
+ payload = ''
+ numberOfObjectsInInvMessage = 0
+ if numberOfObjectsInInvMessage > 0:
+ self.sendinvMessageToJustThisOnePeer(
+ numberOfObjectsInInvMessage, payload)
+
+ # Used to send a big inv message when the connection with a node is
+ # first fully established. Notice that there is also a broadcastinv
+ # function for broadcasting invs to everyone in our stream.
+ def sendinvMessageToJustThisOnePeer(self, numberOfObjects, payload):
+ payload = encodeVarint(numberOfObjects) + payload
+ logger.debug('Sending huge inv message with ' + str(numberOfObjects) + ' objects to just this one peer')
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.CreatePacket('inv', payload)))
+
+ def _sleepForTimingAttackMitigation(self, sleepTime):
+ # We don't need to do the timing attack mitigation if we are
+ # only connected to the trusted peer because we can trust the
+ # peer not to attack
+ if sleepTime > 0 and doTimingAttackMitigation and state.trustedPeer == None:
+ logger.debug('Timing attack mitigation: Sleeping for ' + str(sleepTime) + ' seconds.')
+ time.sleep(sleepTime)
+
+ def recerror(self, data):
+ """
+ The remote node has been polite enough to send you an error message.
+ """
+ fatalStatus, readPosition = decodeVarint(data[:10])
+ banTime, banTimeLength = decodeVarint(data[readPosition:readPosition+10])
+ readPosition += banTimeLength
+ inventoryVectorLength, inventoryVectorLengthLength = decodeVarint(data[readPosition:readPosition+10])
+ if inventoryVectorLength > 100:
+ return
+ readPosition += inventoryVectorLengthLength
+ inventoryVector = data[readPosition:readPosition+inventoryVectorLength]
+ readPosition += inventoryVectorLength
+ errorTextLength, errorTextLengthLength = decodeVarint(data[readPosition:readPosition+10])
+ if errorTextLength > 1000:
+ return
+ readPosition += errorTextLengthLength
+ errorText = data[readPosition:readPosition+errorTextLength]
+ if fatalStatus == 0:
+ fatalHumanFriendly = 'Warning'
+ elif fatalStatus == 1:
+ fatalHumanFriendly = 'Error'
+ elif fatalStatus == 2:
+ fatalHumanFriendly = 'Fatal'
+ message = '%s message received from %s: %s.' % (fatalHumanFriendly, self.peer, errorText)
+ if inventoryVector:
+ message += " This concerns object %s" % hexlify(inventoryVector)
+ if banTime > 0:
+ message += " Remote node says that the ban time is %s" % banTime
+ logger.error(message)
+
+
+ def recobject(self, data):
+ self.messageProcessingStartTime = time.time()
+ lengthOfTimeWeShouldUseToProcessThisMessage = shared.checkAndShareObjectWithPeers(data)
+ self.downloadQueue.task_done(calculateInventoryHash(data))
+
+ """
+ Sleeping will help guarantee that we can process messages faster than a
+ remote node can send them. If we fall behind, the attacker could observe
+ that we are are slowing down the rate at which we request objects from the
+ network which would indicate that we own a particular address (whichever
+ one to which they are sending all of their attack messages). Note
+ that if an attacker connects to a target with many connections, this
+ mitigation mechanism might not be sufficient.
+ """
+ sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - (time.time() - self.messageProcessingStartTime)
+ self._sleepForTimingAttackMitigation(sleepTime)
+
+
+ # We have received an inv message
+ def recinv(self, data):
+ numberOfItemsInInv, lengthOfVarint = decodeVarint(data[:10])
+ if numberOfItemsInInv > 50000:
+ sys.stderr.write('Too many items in inv message!')
+ return
+ if len(data) < lengthOfVarint + (numberOfItemsInInv * 32):
+ logger.info('inv message doesn\'t contain enough data. Ignoring.')
+ return
+
+ startTime = time.time()
+ advertisedSet = set()
+ for i in range(numberOfItemsInInv):
+ advertisedSet.add(data[lengthOfVarint + (32 * i):32 + lengthOfVarint + (32 * i)])
+ objectsNewToMe = advertisedSet
+ for stream in self.streamNumber:
+ objectsNewToMe -= Inventory().hashes_by_stream(stream)
+ logger.info('inv message lists %s objects. Of those %s are new to me. It took %s seconds to figure that out.', numberOfItemsInInv, len(objectsNewToMe), time.time()-startTime)
+ for item in random.sample(objectsNewToMe, len(objectsNewToMe)):
+ self.downloadQueue.put(item)
+
+ # Send a getdata message to our peer to request the object with the given
+ # hash
+ def sendgetdata(self, hashes):
+ if len(hashes) == 0:
+ return
+ logger.debug('sending getdata to retrieve %i objects', len(hashes))
+ payload = encodeVarint(len(hashes)) + ''.join(hashes)
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.CreatePacket('getdata', payload)), False)
+
+
+ # We have received a getdata request from our peer
+ def recgetdata(self, data):
+ numberOfRequestedInventoryItems, lengthOfVarint = decodeVarint(
+ data[:10])
+ if len(data) < lengthOfVarint + (32 * numberOfRequestedInventoryItems):
+ logger.debug('getdata message does not contain enough data. Ignoring.')
+ return
+ self.antiIntersectionDelay(True) # only handle getdata requests if we have been connected long enough
+ for i in xrange(numberOfRequestedInventoryItems):
+ hash = data[lengthOfVarint + (
+ i * 32):32 + lengthOfVarint + (i * 32)]
+ logger.debug('received getdata request for item:' + hexlify(hash))
+
+ if self.objectHashHolderInstance.hasHash(hash):
+ self.antiIntersectionDelay()
+ else:
+ if hash in Inventory():
+ self.sendObject(hash, Inventory()[hash].payload)
+ else:
+ self.antiIntersectionDelay()
+ logger.warning('%s asked for an object with a getdata which is not in either our memory inventory or our SQL inventory. We probably cleaned it out after advertising it but before they got around to asking for it.' % (self.peer,))
+
+ # Our peer has requested (in a getdata message) that we send an object.
+ def sendObject(self, hash, payload):
+ logger.debug('sending an object.')
+ self.sendDataThreadQueue.put((0, 'sendRawData', (hash, protocol.CreatePacket('object',payload))))
+
+ def _checkIPAddress(self, host):
+ if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
+ hostStandardFormat = socket.inet_ntop(socket.AF_INET, host[12:])
+ return self._checkIPv4Address(host[12:], hostStandardFormat)
+ elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43':
+ # Onion, based on BMD/bitcoind
+ hostStandardFormat = base64.b32encode(host[6:]).lower() + ".onion"
+ return hostStandardFormat
+ else:
+ hostStandardFormat = socket.inet_ntop(socket.AF_INET6, host)
+ if hostStandardFormat == "":
+ # This can happen on Windows systems which are not 64-bit compatible
+ # so let us drop the IPv6 address.
+ return False
+ return self._checkIPv6Address(host, hostStandardFormat)
+
+ def _checkIPv4Address(self, host, hostStandardFormat):
+ if host[0] == '\x7F': # 127/8
+ logger.debug('Ignoring IP address in loopback range: ' + hostStandardFormat)
+ return False
+ if host[0] == '\x0A': # 10/8
+ logger.debug('Ignoring IP address in private range: ' + hostStandardFormat)
+ return False
+ if host[0:2] == '\xC0\xA8': # 192.168/16
+ logger.debug('Ignoring IP address in private range: ' + hostStandardFormat)
+ return False
+ if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12
+ logger.debug('Ignoring IP address in private range:' + hostStandardFormat)
+ return False
+ return hostStandardFormat
+
+ def _checkIPv6Address(self, host, hostStandardFormat):
+ if host == ('\x00' * 15) + '\x01':
+ logger.debug('Ignoring loopback address: ' + hostStandardFormat)
+ return False
+ if host[0] == '\xFE' and (ord(host[1]) & 0xc0) == 0x80:
+ logger.debug ('Ignoring local address: ' + hostStandardFormat)
+ return False
+ if (ord(host[0]) & 0xfe) == 0xfc:
+ logger.debug ('Ignoring unique local address: ' + hostStandardFormat)
+ return False
+ return hostStandardFormat
+
+ # We have received an addr message.
+ def recaddr(self, data):
+ numberOfAddressesIncluded, lengthOfNumberOfAddresses = decodeVarint(
+ data[:10])
+
+ if shared.verbose >= 1:
+ logger.debug('addr message contains ' + str(numberOfAddressesIncluded) + ' IP addresses.')
+
+ if numberOfAddressesIncluded > 1000 or numberOfAddressesIncluded == 0:
+ return
+ if len(data) != lengthOfNumberOfAddresses + (38 * numberOfAddressesIncluded):
+ logger.debug('addr message does not contain the correct amount of data. Ignoring.')
+ return
+
+ for i in range(0, numberOfAddressesIncluded):
+ fullHost = data[20 + lengthOfNumberOfAddresses + (38 * i):36 + lengthOfNumberOfAddresses + (38 * i)]
+ recaddrStream, = unpack('>I', data[8 + lengthOfNumberOfAddresses + (
+ 38 * i):12 + lengthOfNumberOfAddresses + (38 * i)])
+ if recaddrStream == 0:
+ continue
+ if recaddrStream not in self.streamNumber and (recaddrStream / 2) not in self.streamNumber: # if the embedded stream number and its parent are not in my streams then ignore it. Someone might be trying funny business.
+ continue
+ recaddrServices, = unpack('>Q', data[12 + lengthOfNumberOfAddresses + (
+ 38 * i):20 + lengthOfNumberOfAddresses + (38 * i)])
+ recaddrPort, = unpack('>H', data[36 + lengthOfNumberOfAddresses + (
+ 38 * i):38 + lengthOfNumberOfAddresses + (38 * i)])
+ hostStandardFormat = self._checkIPAddress(fullHost)
+ if hostStandardFormat is False:
+ continue
+ if recaddrPort == 0:
+ continue
+ timeSomeoneElseReceivedMessageFromThisNode, = unpack('>Q', data[lengthOfNumberOfAddresses + (
+ 38 * i):8 + lengthOfNumberOfAddresses + (38 * i)]) # This is the 'time' value in the received addr message. 64-bit.
+ if recaddrStream not in knownnodes.knownNodes: # knownNodes is a dictionary of dictionaries with one outer dictionary for each stream. If the outer stream dictionary doesn't exist yet then we must make it.
+ with knownnodes.knownNodesLock:
+ knownnodes.knownNodes[recaddrStream] = {}
+ peerFromAddrMessage = state.Peer(hostStandardFormat, recaddrPort)
+ if peerFromAddrMessage not in knownnodes.knownNodes[recaddrStream]:
+ # only if recent
+ if timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800):
+ # bootstrap provider?
+ if BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections') >= \
+ BMConfigParser().safeGetInt('bitmessagesettings', 'maxtotalconnections', 200):
+ knownnodes.trimKnownNodes(recaddrStream)
+ with knownnodes.knownNodesLock:
+ knownnodes.knownNodes[recaddrStream][peerFromAddrMessage] = int(time.time()) - 86400 # penalise initially by 1 day
+ logger.debug('added new node ' + str(peerFromAddrMessage) + ' to knownNodes in stream ' + str(recaddrStream))
+ shared.needToWriteKnownNodesToDisk = True
+ # normal mode
+ elif len(knownnodes.knownNodes[recaddrStream]) < 20000:
+ with knownnodes.knownNodesLock:
+ knownnodes.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode
+ hostDetails = (
+ timeSomeoneElseReceivedMessageFromThisNode,
+ recaddrStream, recaddrServices, hostStandardFormat, recaddrPort)
+ protocol.broadcastToSendDataQueues((
+ recaddrStream, 'advertisepeer', hostDetails))
+ logger.debug('added new node ' + str(peerFromAddrMessage) + ' to knownNodes in stream ' + str(recaddrStream))
+ shared.needToWriteKnownNodesToDisk = True
+ # only update if normal mode
+ elif BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections') < \
+ BMConfigParser().safeGetInt('bitmessagesettings', 'maxtotalconnections', 200):
+ timeLastReceivedMessageFromThisNode = knownnodes.knownNodes[recaddrStream][
+ peerFromAddrMessage]
+ if (timeLastReceivedMessageFromThisNode < timeSomeoneElseReceivedMessageFromThisNode) and (timeSomeoneElseReceivedMessageFromThisNode < int(time.time())+900): # 900 seconds for wiggle-room in case other nodes' clocks aren't quite right.
+ with knownnodes.knownNodesLock:
+ knownnodes.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode
+
+ for stream in self.streamNumber:
+ logger.debug('knownNodes currently has %i nodes for stream %i', len(knownnodes.knownNodes[stream]), stream)
+
+
+ # Send a huge addr message to our peer. This is only used
+ # when we fully establish a connection with a
+ # peer (with the full exchange of version and verack
+ # messages).
+ def sendaddr(self):
+ def sendChunk():
+ if numberOfAddressesInAddrMessage == 0:
+ return
+ self.sendDataThreadQueue.put((0, 'sendRawData', \
+ protocol.CreatePacket('addr', \
+ encodeVarint(numberOfAddressesInAddrMessage) + payload)))
+
+ # 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)
+
+ # protocol defines this as a maximum in one chunk
+ protocolAddrLimit = 1000
+
+ # init
+ numberOfAddressesInAddrMessage = 0
+ payload = ''
+
+ for stream in self.streamNumber:
+ addrsInMyStream = {}
+ addrsInChildStreamLeft = {}
+ addrsInChildStreamRight = {}
+
+ with knownnodes.knownNodesLock:
+ if len(knownnodes.knownNodes[stream]) > 0:
+ filtered = {k: v for k, v in knownnodes.knownNodes[stream].items()
+ if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
+ elemCount = len(filtered)
+ if elemCount > maxAddrCount:
+ elemCount = maxAddrCount
+ # only if more recent than 3 hours
+ addrsInMyStream = random.sample(filtered.items(), elemCount)
+ # sent 250 only if the remote isn't interested in it
+ if len(knownnodes.knownNodes[stream * 2]) > 0 and stream not in self.streamNumber:
+ filtered = {k: v for k, v in knownnodes.knownNodes[stream*2].items()
+ if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
+ elemCount = len(filtered)
+ if elemCount > maxAddrCount / 2:
+ elemCount = int(maxAddrCount / 2)
+ addrsInChildStreamLeft = random.sample(filtered.items(), elemCount)
+ if len(knownnodes.knownNodes[(stream * 2) + 1]) > 0 and stream not in self.streamNumber:
+ filtered = {k: v for k, v in knownnodes.knownNodes[stream*2+1].items()
+ if v > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
+ elemCount = len(filtered)
+ if elemCount > maxAddrCount / 2:
+ elemCount = int(maxAddrCount / 2)
+ addrsInChildStreamRight = random.sample(filtered.items(), elemCount)
+ for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInMyStream:
+ numberOfAddressesInAddrMessage += 1
+ payload += pack(
+ '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time
+ payload += pack('>I', stream)
+ payload += pack(
+ '>q', 1) # service bit flags offered by this node
+ payload += protocol.encodeHost(HOST)
+ payload += pack('>H', PORT) # remote port
+ if numberOfAddressesInAddrMessage >= protocolAddrLimit:
+ sendChunk()
+ payload = ''
+ numberOfAddressesInAddrMessage = 0
+ for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInChildStreamLeft:
+ numberOfAddressesInAddrMessage += 1
+ payload += pack(
+ '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time
+ payload += pack('>I', stream * 2)
+ payload += pack(
+ '>q', 1) # service bit flags offered by this node
+ payload += protocol.encodeHost(HOST)
+ payload += pack('>H', PORT) # remote port
+ if numberOfAddressesInAddrMessage >= protocolAddrLimit:
+ sendChunk()
+ payload = ''
+ numberOfAddressesInAddrMessage = 0
+ for (HOST, PORT), timeLastReceivedMessageFromThisNode in addrsInChildStreamRight:
+ numberOfAddressesInAddrMessage += 1
+ payload += pack(
+ '>Q', timeLastReceivedMessageFromThisNode) # 64-bit time
+ payload += pack('>I', (stream * 2) + 1)
+ payload += pack(
+ '>q', 1) # service bit flags offered by this node
+ payload += protocol.encodeHost(HOST)
+ payload += pack('>H', PORT) # remote port
+ if numberOfAddressesInAddrMessage >= protocolAddrLimit:
+ sendChunk()
+ payload = ''
+ numberOfAddressesInAddrMessage = 0
+
+ # flush
+ sendChunk()
+
+ # We have received a version message
+ def recversion(self, data):
+ if len(data) < 83:
+ # This version message is unreasonably short. Forget it.
+ return
+ if self.verackSent:
+ """
+ We must have already processed the remote node's version message.
+ There might be a time in the future when we Do want to process
+ a new version message, like if the remote node wants to update
+ the streams in which they are interested. But for now we'll
+ ignore this version message
+ """
+ return
+
+ self.remoteProtocolVersion, = unpack('>L', data[:4])
+ self.services, = unpack('>q', data[4:12])
+
+ timestamp, = unpack('>Q', data[12:20])
+ self.timeOffset = timestamp - int(time.time())
+
+ self.myExternalIP = socket.inet_ntoa(data[40:44])
+ # print 'myExternalIP', self.myExternalIP
+ self.remoteNodeIncomingPort, = unpack('>H', data[70:72])
+ # print 'remoteNodeIncomingPort', self.remoteNodeIncomingPort
+ useragentLength, lengthOfUseragentVarint = decodeVarint(
+ data[80:84])
+ readPosition = 80 + lengthOfUseragentVarint
+ self.userAgent = data[readPosition:readPosition + useragentLength]
+
+ # version check
+ try:
+ userAgentName, userAgentVersion = self.userAgent[1:-1].split(":", 2)
+ except:
+ userAgentName = self.userAgent
+ userAgentVersion = "0.0.0"
+ if userAgentName == "PyBitmessage":
+ myVersion = [int(n) for n in softwareVersion.split(".")]
+ try:
+ remoteVersion = [int(n) for n in userAgentVersion.split(".")]
+ except:
+ remoteVersion = 0
+ # remote is newer, but do not cross between stable and unstable
+ try:
+ if cmp(remoteVersion, myVersion) > 0 and \
+ (myVersion[1] % 2 == remoteVersion[1] % 2):
+ queues.UISignalQueue.put(('newVersionAvailable', remoteVersion))
+ except:
+ pass
+
+ readPosition += useragentLength
+ numberOfStreamsInVersionMessage, lengthOfNumberOfStreamsInVersionMessage = decodeVarint(
+ data[readPosition:])
+ readPosition += lengthOfNumberOfStreamsInVersionMessage
+ self.remoteStreams = []
+ for i in range(numberOfStreamsInVersionMessage):
+ newStreamNumber, lengthOfRemoteStreamNumber = decodeVarint(data[readPosition:])
+ readPosition += lengthOfRemoteStreamNumber
+ self.remoteStreams.append(newStreamNumber)
+ logger.debug('Remote node useragent: %s, streams: (%s), time offset: %is.',
+ self.userAgent, ', '.join(str(x) for x in self.remoteStreams), self.timeOffset)
+
+ # find shared streams
+ self.streamNumber = sorted(set(state.streamsInWhichIAmParticipating).intersection(self.remoteStreams))
+
+ shared.connectedHostsList[
+ self.hostIdent] = 1 # We use this data structure to not only keep track of what hosts we are connected to so that we don't try to connect to them again, but also to list the connections count on the Network Status tab.
+ self.sendDataThreadQueue.put((0, 'setStreamNumber', self.remoteStreams))
+ if data[72:80] == protocol.eightBytesOfRandomDataUsedToDetectConnectionsToSelf:
+ self.sendDataThreadQueue.put((0, 'shutdown','no data'))
+ logger.debug('Closing connection to myself: ' + str(self.peer))
+ return
+
+ # The other peer's protocol version is of interest to the sendDataThread but we learn of it
+ # in this version message. Let us inform the sendDataThread.
+ self.sendDataThreadQueue.put((0, 'setRemoteProtocolVersion', self.remoteProtocolVersion))
+
+ if not isHostInPrivateIPRange(self.peer.host):
+ with knownnodes.knownNodesLock:
+ for stream in self.remoteStreams:
+ knownnodes.knownNodes[stream][state.Peer(self.peer.host, self.remoteNodeIncomingPort)] = int(time.time())
+ if not self.initiatedConnection:
+ # bootstrap provider?
+ if BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections') >= \
+ BMConfigParser().safeGetInt('bitmessagesettings', 'maxtotalconnections', 200):
+ knownnodes.knownNodes[stream][state.Peer(self.peer.host, self.remoteNodeIncomingPort)] -= 10800 # penalise inbound, 3 hours
+ else:
+ knownnodes.knownNodes[stream][state.Peer(self.peer.host, self.remoteNodeIncomingPort)] -= 7200 # penalise inbound, 2 hours
+ shared.needToWriteKnownNodesToDisk = True
+
+ self.sendverack()
+ if self.initiatedConnection == False:
+ self.sendversion()
+
+ # Sends a version message
+ def sendversion(self):
+ logger.debug('Sending version message')
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.assembleVersionMessage(
+ self.peer.host, self.peer.port, state.streamsInWhichIAmParticipating, not self.initiatedConnection)))
+
+ # Sends a verack message
+ def sendverack(self):
+ logger.debug('Sending verack')
+ self.sendDataThreadQueue.put((0, 'sendRawData', protocol.CreatePacket('verack')))
+ self.verackSent = True
+ if self.verackReceived:
+ self.connectionFullyEstablished()
diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py
new file mode 100644
index 00000000..792fedd0
--- /dev/null
+++ b/src/class_sendDataThread.py
@@ -0,0 +1,216 @@
+import errno
+import time
+import threading
+import Queue
+from struct import unpack, pack
+import hashlib
+import random
+import select
+import socket
+from ssl import SSLError, SSL_ERROR_WANT_WRITE
+import sys
+
+from helper_generic import addDataPadding
+from class_objectHashHolder import *
+from addresses import *
+from debug import logger
+from inventory import PendingUpload
+import protocol
+import state
+import throttle
+
+# Every connection to a peer has a sendDataThread (and also a
+# receiveDataThread).
+class sendDataThread(threading.Thread):
+
+ def __init__(self, sendDataThreadQueue):
+ threading.Thread.__init__(self, name="sendData")
+ self.sendDataThreadQueue = sendDataThreadQueue
+ state.sendDataQueues.append(self.sendDataThreadQueue)
+ self.data = ''
+ self.objectHashHolderInstance = objectHashHolder(self.sendDataThreadQueue)
+ self.objectHashHolderInstance.daemon = True
+ self.objectHashHolderInstance.start()
+ self.connectionIsOrWasFullyEstablished = False
+
+
+ def setup(
+ self,
+ sock,
+ HOST,
+ PORT,
+ streamNumber
+ ):
+ self.sock = sock
+ self.peer = state.Peer(HOST, PORT)
+ self.name = "sendData-" + self.peer.host.replace(":", ".") # log parser field separator
+ self.streamNumber = []
+ self.services = 0
+ self.buffer = ""
+ self.initiatedConnection = False
+ self.remoteProtocolVersion = - \
+ 1 # This must be set using setRemoteProtocolVersion command which is sent through the self.sendDataThreadQueue queue.
+ self.lastTimeISentData = int(
+ time.time()) # If this value increases beyond five minutes ago, we'll send a pong message to keep the connection alive.
+ if streamNumber == -1: # This was an incoming connection.
+ self.initiatedConnection = False
+ else:
+ self.initiatedConnection = True
+ #logger.debug('The streamNumber of this sendDataThread (ID: ' + str(id(self)) + ') at setup() is' + str(self.streamNumber))
+
+
+ def sendVersionMessage(self):
+ datatosend = protocol.assembleVersionMessage(
+ self.peer.host, self.peer.port, state.streamsInWhichIAmParticipating, not self.initiatedConnection) # the IP and port of the remote host, and my streamNumber.
+
+ logger.debug('Sending version packet: ' + repr(datatosend))
+
+ try:
+ self.sendBytes(datatosend)
+ except Exception as err:
+ # if not 'Bad file descriptor' in err:
+ logger.error('sock.sendall error: %s\n' % err)
+
+ self.versionSent = 1
+
+ def sendBytes(self, data = ""):
+ self.buffer += data
+ if len(self.buffer) < throttle.SendThrottle().chunkSize and self.sendDataThreadQueue.qsize() > 1:
+ return True
+
+ while self.buffer and state.shutdown == 0:
+ isSSL = False
+ try:
+ if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and
+ self.connectionIsOrWasFullyEstablished and
+ protocol.haveSSL(not self.initiatedConnection)):
+ isSSL = True
+ amountSent = self.sslSock.send(self.buffer[:throttle.SendThrottle().chunkSize])
+ else:
+ amountSent = self.sock.send(self.buffer[:throttle.SendThrottle().chunkSize])
+ except socket.timeout:
+ continue
+ except SSLError as e:
+ if e.errno == SSL_ERROR_WANT_WRITE:
+ select.select([], [self.sslSock], [], 10)
+ logger.debug('sock.recv retriable SSL error')
+ continue
+ logger.debug('Connection error (SSL)')
+ return False
+ except socket.error as e:
+ if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK) or \
+ (sys.platform.startswith('win') and \
+ e.errno == errno.WSAEWOULDBLOCK):
+ select.select([], [self.sslSock if isSSL else self.sock], [], 10)
+ logger.debug('sock.recv retriable error')
+ continue
+ if e.errno in (errno.EPIPE, errno.ECONNRESET, errno.EHOSTUNREACH, errno.ETIMEDOUT, errno.ECONNREFUSED):
+ logger.debug('Connection error: %s', str(e))
+ return False
+ raise
+ throttle.SendThrottle().wait(amountSent)
+ self.lastTimeISentData = int(time.time())
+ self.buffer = self.buffer[amountSent:]
+ return True
+
+ def run(self):
+ logger.debug('sendDataThread starting. ID: ' + str(id(self)) + '. Number of queues in sendDataQueues: ' + str(len(state.sendDataQueues)))
+ while self.sendBytes():
+ deststream, command, data = self.sendDataThreadQueue.get()
+
+ if deststream == 0 or deststream in self.streamNumber:
+ if command == 'shutdown':
+ logger.debug('sendDataThread (associated with ' + str(self.peer) + ') ID: ' + str(id(self)) + ' shutting down now.')
+ break
+ # When you receive an incoming connection, a sendDataThread is
+ # created even though you don't yet know what stream number the
+ # remote peer is interested in. They will tell you in a version
+ # message and if you too are interested in that stream then you
+ # will continue on with the connection and will set the
+ # streamNumber of this send data thread here:
+ elif command == 'setStreamNumber':
+ self.streamNumber = data
+ logger.debug('setting the stream number to %s', ', '.join(str(x) for x in self.streamNumber))
+ elif command == 'setRemoteProtocolVersion':
+ specifiedRemoteProtocolVersion = data
+ logger.debug('setting the remote node\'s protocol version in the sendDataThread (ID: ' + str(id(self)) + ') to ' + str(specifiedRemoteProtocolVersion))
+ self.remoteProtocolVersion = specifiedRemoteProtocolVersion
+ elif command == 'advertisepeer':
+ self.objectHashHolderInstance.holdPeer(data)
+ elif command == 'sendaddr':
+ if self.connectionIsOrWasFullyEstablished: # only send addr messages if we have sent and heard a verack from the remote node
+ numberOfAddressesInAddrMessage = len(data)
+ payload = ''
+ for hostDetails in data:
+ timeLastReceivedMessageFromThisNode, streamNumber, services, host, port = hostDetails
+ payload += pack(
+ '>Q', timeLastReceivedMessageFromThisNode) # now uses 64-bit time
+ payload += pack('>I', streamNumber)
+ payload += pack(
+ '>q', services) # service bit flags offered by this node
+ payload += protocol.encodeHost(host)
+ payload += pack('>H', port)
+
+ payload = encodeVarint(numberOfAddressesInAddrMessage) + payload
+ packet = protocol.CreatePacket('addr', payload)
+ try:
+ self.sendBytes(packet)
+ except:
+ logger.error('sendaddr: self.sock.sendall failed')
+ break
+ elif command == 'advertiseobject':
+ self.objectHashHolderInstance.holdHash(data)
+ elif command == 'sendinv':
+ if self.connectionIsOrWasFullyEstablished: # only send inv messages if we have send and heard a verack from the remote node
+ payload = ''
+ for hash in data:
+ payload += hash
+ if payload != '':
+ payload = encodeVarint(len(payload)/32) + payload
+ packet = protocol.CreatePacket('inv', payload)
+ try:
+ self.sendBytes(packet)
+ except:
+ logger.error('sendinv: self.sock.sendall failed')
+ break
+ elif command == 'pong':
+ if self.lastTimeISentData < (int(time.time()) - 298):
+ # Send out a pong message to keep the connection alive.
+ logger.debug('Sending pong to ' + str(self.peer) + ' to keep connection alive.')
+ packet = protocol.CreatePacket('pong')
+ try:
+ self.sendBytes(packet)
+ except:
+ logger.error('send pong failed')
+ break
+ elif command == 'sendRawData':
+ objectHash = None
+ if type(data) in [list, tuple]:
+ objectHash, data = data
+ try:
+ self.sendBytes(data)
+ PendingUpload().delete(objectHash)
+ except:
+ logger.error('Sending of data to ' + str(self.peer) + ' failed. sendDataThread thread ' + str(self) + ' ending now.', exc_info=True)
+ break
+ elif command == 'connectionIsOrWasFullyEstablished':
+ self.connectionIsOrWasFullyEstablished = True
+ self.services, self.sslSock = data
+ elif self.connectionIsOrWasFullyEstablished:
+ logger.error('sendDataThread ID: ' + str(id(self)) + ' ignoring command ' + command + ' because the thread is not in stream ' + str(deststream) + ' but in streams ' + ', '.join(str(x) for x in self.streamNumber))
+ self.sendDataThreadQueue.task_done()
+ # Flush if the cycle ended with break
+ try:
+ self.sendDataThreadQueue.task_done()
+ except ValueError:
+ pass
+
+ try:
+ self.sock.shutdown(socket.SHUT_RDWR)
+ self.sock.close()
+ except:
+ pass
+ state.sendDataQueues.remove(self.sendDataThreadQueue)
+ PendingUpload().threadEnd()
+ logger.info('sendDataThread ending. ID: ' + str(id(self)) + '. Number of queues in sendDataQueues: ' + str(len(state.sendDataQueues)))
+ self.objectHashHolderInstance.close()
diff --git a/src/class_singleListener.py b/src/class_singleListener.py
new file mode 100644
index 00000000..7626542d
--- /dev/null
+++ b/src/class_singleListener.py
@@ -0,0 +1,168 @@
+import threading
+import shared
+import socket
+from bmconfigparser import BMConfigParser
+from class_sendDataThread import *
+from class_receiveDataThread import *
+import helper_bootstrap
+from helper_threading import *
+import protocol
+import errno
+import re
+
+import state
+
+# Only one singleListener thread will ever exist. It creates the
+# receiveDataThread and sendDataThread for each incoming connection. Note
+# that it cannot set the stream number because it is not known yet- the
+# other node will have to tell us its stream number in a version message.
+# If we don't care about their stream, we will close the connection
+# (within the recversion function of the recieveData thread)
+
+
+class singleListener(threading.Thread, StoppableThread):
+
+ def __init__(self):
+ threading.Thread.__init__(self, name="singleListener")
+ self.initStop()
+
+ def setup(self, selfInitiatedConnections):
+ self.selfInitiatedConnections = selfInitiatedConnections
+
+ def _createListenSocket(self, family):
+ HOST = '' # Symbolic name meaning all available interfaces
+ # If not sockslisten, but onionhostname defined, only listen on localhost
+ if not BMConfigParser().safeGetBoolean('bitmessagesettings', 'sockslisten') and ".onion" in BMConfigParser().get('bitmessagesettings', 'onionhostname'):
+ if family == socket.AF_INET6 and "." in BMConfigParser().get('bitmessagesettings', 'onionbindip'):
+ raise socket.error(errno.EINVAL, "Invalid mix of IPv4 and IPv6")
+ elif family == socket.AF_INET and ":" in BMConfigParser().get('bitmessagesettings', 'onionbindip'):
+ raise socket.error(errno.EINVAL, "Invalid mix of IPv4 and IPv6")
+ HOST = BMConfigParser().get('bitmessagesettings', 'onionbindip')
+ PORT = BMConfigParser().getint('bitmessagesettings', 'port')
+ sock = socket.socket(family, socket.SOCK_STREAM)
+ if family == socket.AF_INET6:
+ # Make sure we can accept both IPv4 and IPv6 connections.
+ # This is the default on everything apart from Windows
+ sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
+ # This option apparently avoids the TIME_WAIT state so that we can
+ # rebind faster
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind((HOST, PORT))
+ sock.listen(2)
+ return sock
+
+ def stopThread(self):
+ super(singleListener, self).stopThread()
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ for ip in ('127.0.0.1', BMConfigParser().get('bitmessagesettings', 'onionbindip')):
+ try:
+ s.connect((ip, BMConfigParser().getint('bitmessagesettings', 'port')))
+ s.shutdown(socket.SHUT_RDWR)
+ s.close()
+ break
+ except:
+ pass
+
+ def run(self):
+ # If there is a trusted peer then we don't want to accept
+ # incoming connections so we'll just abandon the thread
+ if state.trustedPeer:
+ return
+
+ while BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect') and state.shutdown == 0:
+ self.stop.wait(1)
+ helper_bootstrap.dns()
+ # We typically don't want to accept incoming connections if the user is using a
+ # SOCKS proxy, unless they have configured otherwise. If they eventually select
+ # proxy 'none' or configure SOCKS listening then this will start listening for
+ # connections. But if on SOCKS and have an onionhostname, listen
+ # (socket is then only opened for localhost)
+ while BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and \
+ (not BMConfigParser().getboolean('bitmessagesettings', 'sockslisten') and \
+ ".onion" not in BMConfigParser().get('bitmessagesettings', 'onionhostname')) and \
+ state.shutdown == 0:
+ self.stop.wait(5)
+
+ logger.info('Listening for incoming connections.')
+
+ # First try listening on an IPv6 socket. This should also be
+ # able to accept connections on IPv4. If that's not available
+ # we'll fall back to IPv4-only.
+ try:
+ sock = self._createListenSocket(socket.AF_INET6)
+ except socket.error as e:
+ if (isinstance(e.args, tuple) and
+ e.args[0] in (errno.EAFNOSUPPORT,
+ errno.EPFNOSUPPORT,
+ errno.EADDRNOTAVAIL,
+ errno.ENOPROTOOPT,
+ errno.EINVAL)):
+ sock = self._createListenSocket(socket.AF_INET)
+ else:
+ raise
+
+ # regexp to match an IPv4-mapped IPv6 address
+ mappedAddressRegexp = re.compile(r'^::ffff:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$')
+
+ while state.shutdown == 0:
+ # We typically don't want to accept incoming connections if the user is using a
+ # SOCKS proxy, unless they have configured otherwise. If they eventually select
+ # proxy 'none' or configure SOCKS listening then this will start listening for
+ # connections.
+ while BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and not BMConfigParser().getboolean('bitmessagesettings', 'sockslisten') and ".onion" not in BMConfigParser().get('bitmessagesettings', 'onionhostname') and state.shutdown == 0:
+ self.stop.wait(10)
+ while len(shared.connectedHostsList) > \
+ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections", 200) + \
+ BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections", 20) \
+ and state.shutdown == 0:
+ logger.info('We are connected to too many people. Not accepting further incoming connections for ten seconds.')
+
+ self.stop.wait(10)
+
+ while state.shutdown == 0:
+ try:
+ socketObject, sockaddr = sock.accept()
+ except socket.error as e:
+ if isinstance(e.args, tuple) and \
+ e.args[0] in (errno.EINTR,):
+ continue
+ time.wait(1)
+ continue
+
+ (HOST, PORT) = sockaddr[0:2]
+
+ # If the address is an IPv4-mapped IPv6 address then
+ # convert it to just the IPv4 representation
+ md = mappedAddressRegexp.match(HOST)
+ if md != None:
+ HOST = md.group(1)
+
+ # The following code will, unfortunately, block an
+ # incoming connection if someone else on the same LAN
+ # is already connected because the two computers will
+ # share the same external IP. This is here to prevent
+ # connection flooding.
+ # permit repeated connections from Tor
+ if HOST in shared.connectedHostsList and \
+ (".onion" not in BMConfigParser().get('bitmessagesettings', 'onionhostname') or not protocol.checkSocksIP(HOST)):
+ socketObject.close()
+ logger.info('We are already connected to ' + str(HOST) + '. Ignoring connection.')
+ else:
+ break
+
+ sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection.
+ socketObject.settimeout(20)
+
+ sd = sendDataThread(sendDataThreadQueue)
+ sd.setup(
+ socketObject, HOST, PORT, -1)
+ sd.start()
+
+ rd = receiveDataThread()
+ rd.daemon = True # close the main program even if there are threads left
+ rd.setup(
+ socketObject, HOST, PORT, -1, self.selfInitiatedConnections, sendDataThreadQueue, sd.objectHashHolderInstance)
+ rd.start()
+
+ logger.info('connected to ' + HOST + ' during INCOMING request.')
+
diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py
index 90db23c3..322bb20e 100644
--- a/src/class_singleWorker.py
+++ b/src/class_singleWorker.py
@@ -19,7 +19,7 @@ import helper_inbox
from helper_generic import addDataPadding
import helper_msgcoding
from helper_threading import *
-from inventory import Inventory
+from inventory import Inventory, PendingUpload
import l10n
import protocol
import queues
@@ -199,6 +199,7 @@ class singleWorker(threading.Thread, StoppableThread):
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime,'')
+ PendingUpload().add(inventoryHash)
logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash))
@@ -288,6 +289,7 @@ class singleWorker(threading.Thread, StoppableThread):
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime,'')
+ PendingUpload().add(inventoryHash)
logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash))
@@ -377,6 +379,7 @@ class singleWorker(threading.Thread, StoppableThread):
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, doubleHashOfAddressData[32:])
+ PendingUpload().add(inventoryHash)
logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash))
@@ -507,6 +510,7 @@ class singleWorker(threading.Thread, StoppableThread):
objectType = 3
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, tag)
+ PendingUpload().add(inventoryHash)
logger.info('sending inv (within sendBroadcast function) for object: ' + hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash))
@@ -830,6 +834,7 @@ class singleWorker(threading.Thread, StoppableThread):
objectType = 2
Inventory()[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload, embeddedTime, '')
+ PendingUpload().add(inventoryHash)
if BMConfigParser().has_section(toaddress) or not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK):
queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr._translate("MainWindow", "Message sent. Sent at %1").arg(l10n.formatTimestamp()))))
else:
@@ -936,6 +941,7 @@ class singleWorker(threading.Thread, StoppableThread):
objectType = 1
Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '')
+ PendingUpload().add(inventoryHash)
logger.info('sending inv (for the getpubkey message)')
queues.invQueue.put((streamNumber, inventoryHash))
diff --git a/src/depends.py b/src/depends.py
index 04656530..d66663b1 100755
--- a/src/depends.py
+++ b/src/depends.py
@@ -93,7 +93,7 @@ def check_openssl():
import os.path
paths.insert(0, os.path.join(sys._MEIPASS, 'libeay32.dll'))
else:
- paths = ['libcrypto.so', 'libcrypto.so.1.0.0']
+ paths = ['libcrypto.so']
if sys.platform == 'darwin':
paths.extend([
'libcrypto.dylib',
diff --git a/src/inventory.py b/src/inventory.py
index 7432b0f1..598021fb 100644
--- a/src/inventory.py
+++ b/src/inventory.py
@@ -1,4 +1,12 @@
+import collections
+from importlib import import_module
+from threading import current_thread, enumerate as threadingEnumerate, RLock
+import Queue
+import time
+import sys
+
from bmconfigparser import BMConfigParser
+from helper_sql import *
from singleton import Singleton
# TODO make this dynamic, and watch out for frozen, like with messagetypes
@@ -10,7 +18,10 @@ class Inventory():
def __init__(self):
#super(self.__class__, self).__init__()
self._moduleName = BMConfigParser().safeGet("inventory", "storage")
- self._inventoryClass = getattr(getattr(storage, self._moduleName), "{}Inventory".format(self._moduleName.title()))
+ #import_module("." + self._moduleName, "storage")
+ #import_module("storage." + self._moduleName)
+ self._className = "storage." + self._moduleName + "." + self._moduleName.title() + "Inventory"
+ self._inventoryClass = eval(self._className)
self._realInventory = self._inventoryClass()
self.numberOfInventoryLookupsPerformed = 0
@@ -24,3 +35,164 @@ class Inventory():
raise AttributeError("%s instance has no attribute '%s'" %(self.__class__.__name__, attr))
else:
return realRet
+
+
+class PendingDownloadQueue(Queue.Queue):
+# keep a track of objects that have been advertised to us but we haven't downloaded them yet
+ maxWait = 300
+
+ def __init__(self, maxsize=0):
+ Queue.Queue.__init__(self, maxsize)
+ self.stopped = False
+ self.pending = {}
+ self.lock = RLock()
+
+ def task_done(self, hashId):
+ Queue.Queue.task_done(self)
+ try:
+ with self.lock:
+ del self.pending[hashId]
+ except KeyError:
+ pass
+
+ def get(self, block=True, timeout=None):
+ retval = Queue.Queue.get(self, block, timeout)
+ # no exception was raised
+ if not self.stopped:
+ with self.lock:
+ self.pending[retval] = time.time()
+ return retval
+
+ def clear(self):
+ with self.lock:
+ newPending = {}
+ for hashId in self.pending:
+ if self.pending[hashId] + PendingDownloadQueue.maxWait > time.time():
+ newPending[hashId] = self.pending[hashId]
+ self.pending = newPending
+
+ @staticmethod
+ def totalSize():
+ size = 0
+ for thread in threadingEnumerate():
+ if thread.isAlive() and hasattr(thread, 'downloadQueue'):
+ size += thread.downloadQueue.qsize() + len(thread.downloadQueue.pending)
+ return size
+
+ @staticmethod
+ def stop():
+ for thread in threadingEnumerate():
+ if thread.isAlive() and hasattr(thread, 'downloadQueue'):
+ thread.downloadQueue.stopped = True
+ with thread.downloadQueue.lock:
+ thread.downloadQueue.pending = {}
+
+
+class PendingUploadDeadlineException(Exception):
+ pass
+
+
+@Singleton
+class PendingUpload(object):
+# keep a track of objects that we have created but haven't distributed yet
+ def __init__(self):
+ super(self.__class__, self).__init__()
+ self.lock = RLock()
+ self.hashes = {}
+ # end by this time in any case
+ self.deadline = 0
+ self.maxLen = 0
+ # during shutdown, wait up to 20 seconds to finish uploading
+ self.shutdownWait = 20
+ # forget tracking objects after 60 seconds
+ self.objectWait = 60
+ # wait 10 seconds between clears
+ self.clearDelay = 10
+ self.lastCleared = time.time()
+
+ def add(self, objectHash = None):
+ with self.lock:
+ # add a new object into existing thread lists
+ if objectHash:
+ if objectHash not in self.hashes:
+ self.hashes[objectHash] = {'created': time.time(), 'sendCount': 0, 'peers': []}
+ for thread in threadingEnumerate():
+ if thread.isAlive() and hasattr(thread, 'peer') and \
+ thread.peer not in self.hashes[objectHash]['peers']:
+ self.hashes[objectHash]['peers'].append(thread.peer)
+ # add all objects into the current thread
+ else:
+ for objectHash in self.hashes:
+ if current_thread().peer not in self.hashes[objectHash]['peers']:
+ self.hashes[objectHash]['peers'].append(current_thread().peer)
+
+ def len(self):
+ self.clearHashes()
+ with self.lock:
+ return sum(1
+ for x in self.hashes if (self.hashes[x]['created'] + self.objectWait < time.time() or
+ self.hashes[x]['sendCount'] == 0))
+
+ def _progress(self):
+ with self.lock:
+ return float(sum(len(self.hashes[x]['peers'])
+ for x in self.hashes if (self.hashes[x]['created'] + self.objectWait < time.time()) or
+ self.hashes[x]['sendCount'] == 0))
+
+ def progress(self, raiseDeadline=True):
+ if self.maxLen < self._progress():
+ self.maxLen = self._progress()
+ if self.deadline < time.time():
+ if self.deadline > 0 and raiseDeadline:
+ raise PendingUploadDeadlineException
+ self.deadline = time.time() + 20
+ try:
+ return 1.0 - self._progress() / self.maxLen
+ except ZeroDivisionError:
+ return 1.0
+
+ def clearHashes(self, objectHash=None):
+ if objectHash is None:
+ if self.lastCleared > time.time() - self.clearDelay:
+ return
+ objects = self.hashes.keys()
+ else:
+ objects = objectHash,
+ with self.lock:
+ for i in objects:
+ try:
+ if self.hashes[i]['sendCount'] > 0 and (
+ len(self.hashes[i]['peers']) == 0 or
+ self.hashes[i]['created'] + self.objectWait < time.time()):
+ del self.hashes[i]
+ except KeyError:
+ pass
+ self.lastCleared = time.time()
+
+ def delete(self, objectHash=None):
+ if not hasattr(current_thread(), 'peer'):
+ return
+ if objectHash is None:
+ return
+ with self.lock:
+ try:
+ if objectHash in self.hashes and current_thread().peer in self.hashes[objectHash]['peers']:
+ self.hashes[objectHash]['sendCount'] += 1
+ self.hashes[objectHash]['peers'].remove(current_thread().peer)
+ except KeyError:
+ pass
+ self.clearHashes(objectHash)
+
+ def stop(self):
+ with self.lock:
+ self.hashes = {}
+
+ def threadEnd(self):
+ with self.lock:
+ for objectHash in self.hashes:
+ try:
+ if current_thread().peer in self.hashes[objectHash]['peers']:
+ self.hashes[objectHash]['peers'].remove(current_thread().peer)
+ except KeyError:
+ pass
+ self.clearHashes()
diff --git a/src/messagetypes/__init__.py b/src/messagetypes/__init__.py
index 06783eac..1a5223df 100644
--- a/src/messagetypes/__init__.py
+++ b/src/messagetypes/__init__.py
@@ -3,7 +3,6 @@ from os import path, listdir
from string import lower
from debug import logger
-import messagetypes
import paths
class MsgBase(object):
@@ -16,8 +15,9 @@ def constructObject(data):
if data[""] not in whitelist:
return None
try:
- classBase = getattr(getattr(messagetypes, data[""]), data[""].title())
- except (NameError, AttributeError):
+ m = import_module("messagetypes." + data[""])
+ classBase = getattr(m, data[""].title())
+ except (NameError, ImportError):
logger.error("Don't know how to handle message type: \"%s\"", data[""], exc_info=True)
return None
try:
@@ -43,7 +43,7 @@ else:
if splitted[1] != ".py":
continue
try:
- import_module(".{}".format(splitted[0]), "messagetypes")
+ import_module("." + splitted[0], "messagetypes")
except ImportError:
logger.error("Error importing %s", mod, exc_info=True)
else:
diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py
index d426dbe8..6f857398 100644
--- a/src/network/advanceddispatcher.py
+++ b/src/network/advanceddispatcher.py
@@ -7,12 +7,6 @@ from debug import logger
from helper_threading import BusyError, nonBlocking
import state
-class ProcessingError(Exception):
- pass
-
-class UnknownStateError(ProcessingError):
- pass
-
class AdvancedDispatcher(asyncore.dispatcher):
_buf_len = 131072 # 128kB
@@ -64,13 +58,11 @@ class AdvancedDispatcher(asyncore.dispatcher):
break
if len(self.read_buf) < self.expectBytes:
return False
- try:
- cmd = getattr(self, "state_" + str(self.state))
- except AttributeError:
- logger.error("Unknown state %s", self.state, exc_info=True)
- raise UnknownState(self.state)
- if not cmd():
+ if not getattr(self, "state_" + str(self.state))():
break
+ except AttributeError:
+ logger.error("Unknown state %s", self.state, exc_info=True)
+ raise
except BusyError:
return False
return False
diff --git a/src/network/objectracker.py b/src/network/objectracker.py
index ff2a9036..66b0685b 100644
--- a/src/network/objectracker.py
+++ b/src/network/objectracker.py
@@ -54,7 +54,8 @@ class ObjectTracker(object):
def clean(self):
if self.lastCleaned < time.time() - ObjectTracker.invCleanPeriod:
if haveBloom:
- if len(missingObjects) == 0:
+ # FIXME
+ if PendingDownloadQueue().size() == 0:
self.initInvBloom()
self.initAddrBloom()
else:
diff --git a/src/network/proxy.py b/src/network/proxy.py
index 1d7ca357..43298f63 100644
--- a/src/network/proxy.py
+++ b/src/network/proxy.py
@@ -3,7 +3,6 @@ import time
from advanceddispatcher import AdvancedDispatcher
import asyncore_pollchoose as asyncore
-from bmconfigparser import BMConfigParser
from debug import logger
import network.connectionpool
import state
@@ -88,11 +87,6 @@ class Proxy(AdvancedDispatcher):
self.isOutbound = True
self.fullyEstablished = False
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
- if BMConfigParser().safeGetBoolean("bitmessagesettings", "socksauthentication"):
- self.auth = (BMConfigParser().safeGet("bitmessagesettings", "socksusername"),
- BMConfigParser().safeGet("bitmessagesettings", "sockspassword"))
- else:
- self.auth = None
if address.host.endswith(".onion") and self.onion_proxy is not None:
self.connect(self.onion_proxy)
else:
diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py
index 0a7562cb..5399b972 100644
--- a/src/network/receivequeuethread.py
+++ b/src/network/receivequeuethread.py
@@ -12,7 +12,6 @@ from helper_threading import StoppableThread
from inventory import Inventory
from network.connectionpool import BMConnectionPool
from network.bmproto import BMProto
-from network.advanceddispatcher import UnknownStateError
from queues import receiveDataQueue
import protocol
import state
@@ -41,21 +40,14 @@ class ReceiveQueueThread(threading.Thread, StoppableThread):
# or the connection is to be aborted
try:
- connection = BMConnectionPool().getConnectionByAddr(dest)
+ BMConnectionPool().getConnectionByAddr(dest).process()
# KeyError = connection object not found
- except KeyError:
- receiveDataQueue.task_done()
- continue
- try:
- connection.process()
- # UnknownStateError = state isn't implemented
- except (UnknownStateError):
+ # AttributeError = state isn't implemented
+ except (KeyError, AttributeError):
pass
except socket.error as err:
if err.errno == errno.EBADF:
- connection.set_state("close", 0)
+ BMConnectionPool().getConnectionByAddr(dest).set_state("close", 0)
else:
logger.error("Socket error: %s", str(err))
- except:
- logger.error("Error processing", exc_info=True)
receiveDataQueue.task_done()
diff --git a/src/network/socks5.py b/src/network/socks5.py
index 52136bdc..52050ec9 100644
--- a/src/network/socks5.py
+++ b/src/network/socks5.py
@@ -39,7 +39,7 @@ class Socks5(Proxy):
return True
def state_auth_1(self):
- ret = struct.unpack('BB', self.read_buf[:2])
+ ret = struct.unpack('BB', self.read_buf)
if ret[0] != 5:
# general error
raise GeneralProxyError(1)
diff --git a/src/plugins/menu_qrcode.py b/src/plugins/menu_qrcode.py
deleted file mode 100644
index 3831e89b..00000000
--- a/src/plugins/menu_qrcode.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-A menu plugin showing QR-Code for bitmessage address in modal dialog.
-"""
-
-from PyQt4 import QtGui, QtCore
-import qrcode
-
-from pybitmessage.tr import _translate
-
-
-# http://stackoverflow.com/questions/20452486
-class Image(qrcode.image.base.BaseImage):
- """Image output class for qrcode using QPainter"""
- def __init__(self, border, width, box_size):
- self.border = border
- self.width = width
- self.box_size = box_size
- size = (width + border * 2) * box_size
- self._image = QtGui.QImage(
- size, size, QtGui.QImage.Format_RGB16)
- self._image.fill(QtCore.Qt.white)
-
- def pixmap(self):
- """Get image pixmap"""
- return QtGui.QPixmap.fromImage(self._image)
-
- def drawrect(self, row, col):
- """Draw a single rectangle - implementation"""
- painter = QtGui.QPainter(self._image)
- painter.fillRect(
- (col + self.border) * self.box_size,
- (row + self.border) * self.box_size,
- self.box_size, self.box_size,
- QtCore.Qt.black)
-
-
-class QRCodeDialog(QtGui.QDialog):
- """The dialog"""
- def __init__(self, parent):
- super(QRCodeDialog, self).__init__(parent)
- self.image = QtGui.QLabel(self)
- self.label = QtGui.QLabel(self)
- font = QtGui.QFont()
- font.setBold(True)
- font.setWeight(75)
- self.label.setFont(font)
- self.label.setAlignment(
- QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter)
- buttonBox = QtGui.QDialogButtonBox(self)
- buttonBox.setOrientation(QtCore.Qt.Horizontal)
- buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
- buttonBox.accepted.connect(self.accept)
- layout = QtGui.QVBoxLayout(self)
- layout.addWidget(self.image)
- layout.addWidget(self.label)
- layout.addWidget(buttonBox)
- self.retranslateUi()
-
- def retranslateUi(self):
- """A conventional Qt Designer method for dynamic l10n"""
- self.setWindowTitle(_translate("QRCodeDialog", "QR-code"))
-
- def render(self, text):
- """Draw QR-code and address in labels"""
- self.label.setText(text)
- self.image.setPixmap(
- qrcode.make(text, image_factory=Image).pixmap())
- self.setFixedSize(QtGui.QWidget.sizeHint(self))
-
-
-def connect_plugin(form):
- """Plugin entry point"""
- def on_action_ShowQR():
- """A slot for popup menu action"""
- try:
- dialog = form.qrcode_dialog
- except AttributeError:
- form.qrcode_dialog = dialog = QRCodeDialog(form)
- dialog.render('bitmessage:' + str(form.getCurrentAccount()))
- dialog.exec_()
-
- return on_action_ShowQR, _translate("MainWindow", "Show QR-code")
diff --git a/src/plugins/qrcodeui.py b/src/plugins/qrcodeui.py
new file mode 100644
index 00000000..25b1e0b1
--- /dev/null
+++ b/src/plugins/qrcodeui.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+
+from PyQt4 import QtGui, QtCore
+import qrcode
+
+from pybitmessage.tr import translateText
+
+try:
+ _fromUtf8 = QtCore.QString.fromUtf8
+except AttributeError:
+ _fromUtf8 = lambda s: s
+
+
+# http://stackoverflow.com/questions/20452486
+class Image(qrcode.image.base.BaseImage):
+ def __init__(self, border, width, box_size):
+ self.border = border
+ self.width = width
+ self.box_size = box_size
+ size = (width + border * 2) * box_size
+ self._image = QtGui.QImage(
+ size, size, QtGui.QImage.Format_RGB16)
+ self._image.fill(QtCore.Qt.white)
+
+ def pixmap(self):
+ return QtGui.QPixmap.fromImage(self._image)
+
+ def drawrect(self, row, col):
+ painter = QtGui.QPainter(self._image)
+ painter.fillRect(
+ (col + self.border) * self.box_size,
+ (row + self.border) * self.box_size,
+ self.box_size, self.box_size,
+ QtCore.Qt.black)
+
+ def save(self, stream, kind=None):
+ pass
+
+
+class Ui_qrcodeDialog(object):
+ def setupUi(self, qrcodeDialog):
+ qrcodeDialog.setObjectName(_fromUtf8("qrcodeDialog"))
+ self.image = QtGui.QLabel(qrcodeDialog)
+ self.label = QtGui.QLabel(qrcodeDialog)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.label.setFont(font)
+ self.label.setAlignment(
+ QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter)
+ self.buttonBox = QtGui.QDialogButtonBox(qrcodeDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
+ layout = QtGui.QVBoxLayout(qrcodeDialog)
+ layout.addWidget(self.image)
+ layout.addWidget(self.label)
+ layout.addWidget(self.buttonBox)
+
+ self.retranslateUi(qrcodeDialog)
+ QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(
+ _fromUtf8("accepted()")), qrcodeDialog.accept)
+ QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(
+ _fromUtf8("rejected()")), qrcodeDialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(qrcodeDialog)
+
+ def retranslateUi(self, qrcodeDialog):
+ qrcodeDialog.setWindowTitle(QtGui.QApplication.translate(
+ "qrcodeDialog", "QR-code",
+ None, QtGui.QApplication.UnicodeUTF8
+ ))
+
+ def render(self, text):
+ self.label.setText(text)
+ self.image.setPixmap(
+ qrcode.make(text, image_factory=Image).pixmap())
+
+
+class qrcodeDialog(QtGui.QDialog):
+
+ def __init__(self, parent):
+ QtGui.QWidget.__init__(self, parent)
+ self.ui = Ui_qrcodeDialog()
+ self.ui.setupUi(self)
+ self.parent = parent
+ QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
+
+
+def connect_plugin(form):
+ def on_action_ShowQR():
+ form.qrcodeDialogInstance = qrcodeDialog(form)
+ form.qrcodeDialogInstance.ui.render(
+ str(form.getCurrentAccount())
+ )
+ form.qrcodeDialogInstance.exec_()
+
+ form.actionShowQRCode = \
+ form.ui.addressContextMenuToolbarYourIdentities.addAction(
+ translateText("MainWindow", "Show QR-code"),
+ on_action_ShowQR
+ )
+ form.popMenuYourIdentities.addAction(form.actionShowQRCode)
diff --git a/src/pyelliptic/openssl.py b/src/pyelliptic/openssl.py
index 115bdc08..7af4fd18 100644
--- a/src/pyelliptic/openssl.py
+++ b/src/pyelliptic/openssl.py
@@ -534,8 +534,6 @@ def loadOpenSSL():
else:
libdir.append('libcrypto.so')
libdir.append('libssl.so')
- libdir.append('libcrypto.so.1.0.0')
- libdir.append('libssl.so.1.0.0')
if 'linux' in sys.platform or 'darwin' in sys.platform or 'bsd' in sys.platform:
libdir.append(find_library('ssl'))
elif 'win32' in sys.platform or 'win64' in sys.platform:
diff --git a/src/throttle.py b/src/throttle.py
new file mode 100644
index 00000000..6e53f217
--- /dev/null
+++ b/src/throttle.py
@@ -0,0 +1,81 @@
+import math
+import threading
+import time
+
+from bmconfigparser import BMConfigParser
+from singleton import Singleton
+import state
+
+class Throttle(object):
+ minChunkSize = 4096
+ maxChunkSize = 131072
+
+ def __init__(self, limit=0):
+ self.limit = limit
+ self.speed = 0
+ self.chunkSize = Throttle.maxChunkSize
+ self.txTime = int(time.time())
+ self.txLen = 0
+ self.total = 0
+ self.timer = threading.Event()
+ self.lock = threading.RLock()
+ self.resetChunkSize()
+
+ def recalculate(self):
+ with self.lock:
+ now = int(time.time())
+ if now > self.txTime:
+ self.speed = self.txLen / (now - self.txTime)
+ self.txLen -= self.limit * (now - self.txTime)
+ self.txTime = now
+ if self.txLen < 0 or self.limit == 0:
+ self.txLen = 0
+
+ def wait(self, dataLen):
+ with self.lock:
+ self.txLen += dataLen
+ self.total += dataLen
+ while state.shutdown == 0:
+ self.recalculate()
+ if self.limit == 0:
+ break
+ if self.txLen < self.limit:
+ break
+ self.timer.wait(0.2)
+
+ def getSpeed(self):
+ self.recalculate()
+ return self.speed
+
+ def resetChunkSize(self):
+ with self.lock:
+ # power of two smaller or equal to speed limit
+ try:
+ self.chunkSize = int(math.pow(2, int(math.log(self.limit,2))))
+ except ValueError:
+ self.chunkSize = Throttle.maxChunkSize
+ # range check
+ if self.chunkSize < Throttle.minChunkSize:
+ self.chunkSize = Throttle.minChunkSize
+ elif self.chunkSize > Throttle.maxChunkSize:
+ self.chunkSize = Throttle.maxChunkSize
+
+@Singleton
+class SendThrottle(Throttle):
+ def __init__(self):
+ Throttle.__init__(self, BMConfigParser().safeGetInt('bitmessagesettings', 'maxuploadrate')*1024)
+
+ def resetLimit(self):
+ with self.lock:
+ self.limit = BMConfigParser().safeGetInt('bitmessagesettings', 'maxuploadrate')*1024
+ Throttle.resetChunkSize(self)
+
+@Singleton
+class ReceiveThrottle(Throttle):
+ def __init__(self):
+ Throttle.__init__(self, BMConfigParser().safeGetInt('bitmessagesettings', 'maxdownloadrate')*1024)
+
+ def resetLimit(self):
+ with self.lock:
+ self.limit = BMConfigParser().safeGetInt('bitmessagesettings', 'maxdownloadrate')*1024
+ Throttle.resetChunkSize(self)
diff --git a/src/translations/bitmessage.pro b/src/translations/bitmessage.pro
index ed491b3d..f5d6b946 100644
--- a/src/translations/bitmessage.pro
+++ b/src/translations/bitmessage.pro
@@ -1,8 +1,12 @@
SOURCES = ../addresses.py\
../bitmessagemain.py\
../class_addressGenerator.py\
+ ../class_outgoingSynSender.py\
../class_objectProcessor.py\
+ ../class_receiveDataThread.py\
+ ../class_sendDataThread.py\
../class_singleCleaner.py\
+ ../class_singleListener.py\
../class_singleWorker.py\
../class_sqlThread.py\
../helper_bitcoin.py\
diff --git a/src/translations/bitmessage_eo.qm b/src/translations/bitmessage_eo.qm
index fa6a1a96..ea03b1b5 100644
Binary files a/src/translations/bitmessage_eo.qm and b/src/translations/bitmessage_eo.qm differ
diff --git a/src/translations/bitmessage_eo.ts b/src/translations/bitmessage_eo.ts
index de853696..beff4aca 100644
--- a/src/translations/bitmessage_eo.ts
+++ b/src/translations/bitmessage_eo.ts
@@ -60,27 +60,27 @@
@mailchuck.com
-
+
Registrado malsukcesis:
-
+
La dezirata retpoŝtadreso ne estas disponebla, bonvolu provi alian.
-
+
Sendado de peto pri registrado ĉe retpoŝta kluzo
-
+
Sendado de peto pri malregistrado de retpoŝta kluzo
-
+
Sendado de peto pri stato de retpoŝta kluzo
@@ -195,52 +195,52 @@ Please type the desired email address (including @mailchuck.com) below:
MainWindow
-
+
Respondi al sendinto
-
+
Respondi al kanalo
-
+
Aldoni sendinton al via adresaro
-
+
Aldoni sendinton al via nigra listo
-
+
Movi al rubujo
-
+
Malforigi
-
+
Montri HTML-n kiel aranĝitan tekston
-
+
Konservi mesaĝon kiel…
-
+
Marki kiel nelegitan
-
+
Nova
@@ -265,12 +265,12 @@ Please type the desired email address (including @mailchuck.com) below:
Kopii adreson al tondejo
-
+
Speciala sinteno de adreso…
-
+
Retpoŝta kluzo
@@ -280,37 +280,37 @@ Please type the desired email address (including @mailchuck.com) below:
Forigi
-
+
Sendi mesaĝon al tiu adreso
-
+
Aboni tiun adreson
-
+
Aldoni novan adreson
-
+
Kopii cel-adreson al tondejo
-
+
Devigi sendadon
-
+
Iu de viaj adresoj, %1, estas malnova versio 1 adreso. Versioj 1 adresoj ne estas jam subtenataj. Ĉu ni povas forigi ĝin?
-
+
Atendado je ilia ĉifroŝlosilo. Baldaŭ petos ĝin denove.
@@ -320,17 +320,17 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
En atendovico.
-
+
Mesaĝo sendita. Atendado je konfirmo. Sendita je %1
-
+
Mesaĝo sendita. Sendita je %1
@@ -340,77 +340,77 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
Ricevis konfirmon de la mesaĝo je %1
-
+
Elsendo en atendovico.
-
+
Elsendo je %1
-
+
Problemo: la demandita laboro de la ricevonto estas pli malfacila ol vi pretas fari. %1
-
+
Problemo: la ĉifroŝlosilo de la ricevonto estas rompita. Ne povis ĉifri la mesaĝon. %1
-
+
Devigita superado de limito de malfacilaĵo. Sendado devus baldaŭ komenci.
-
+
Nekonata stato: %1 %2
-
+
Ne konektita
-
+
Montri Bitmesaĝon
-
+
Sendi
-
+
Aboni
-
+
Kanalo
-
+
Eliri
-
+
Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava, ke vi faru sekurkopion de tiu dosiero.
-
+
@@ -419,17 +419,17 @@ It is important that you back up this file.
Estas grava, ke vi faru sekurkopion de tiu dosiero.
-
+
Ĉu malfermi keys.dat?
-
+
Vi povas administri viajn ŝlosilojn per redakti la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.)
-
+
@@ -438,37 +438,37 @@ It is important that you back up this file. Would you like to open the file now?
Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.)
-
+
Ĉu malplenigi rubujon?
-
+
Ĉu vi certe volas forviŝi ĉiujn mesaĝojn el la rubujo?
-
+
malprava pasvorto
-
+
Vi devas tajpi vian pasvorton. Se vi ne havas pasvorton, tiu ĉi ne estas la prava formularo por vi.
-
+
Erara numero de adresversio
-
+
Via numero de adresversio devas esti: aŭ 3 aŭ 4.
-
+
Via numero de adresversio devas esti: aŭ 3 aŭ 4.
@@ -538,22 +538,22 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
-
+
Perdis konekton
-
+
Konektita
-
+
Movis mesaĝon al rubujo
-
+
-
+
Mesaĝo tro longa
-
+
La mesaĝon kiun vi provis sendi estas tro longa je %1 bitokoj. (La maksimumo estas 261644 bitokoj.) Bonvolu mallongigi ĝin antaŭ sendado.
-
+
Eraro: Via konto ne estas registrita je retpoŝta kluzo. Registranta nun kiel %1, bonvolu atendi ĝis la registrado finos antaŭ vi reprovos sendi iun ajn.
@@ -616,57 +616,57 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
-
+
Eraro: Vi devas elekti sendontan adreson. Se vi ne havas iun, iru al langeto 'Viaj identigoj'.
-
+
Numero de adresversio
-
+
Dum prilaborado de adreso adreso %1, Bitmesaĝo ne povas kompreni numerojn %2 de adresversioj. Eble ĝisdatigu Bitmesaĝon al la plej nova versio.
-
+
Fluo numero
-
+
Dum prilaborado de adreso %1, Bitmesaĝo ne povas priservi %2 fluojn numerojn. Eble ĝisdatigu Bitmesaĝon al la plej nova versio.
-
+
Atentu: Vi ne estas nun konektita. Bitmesaĝo faros necesan laboron por sendi mesaĝon, tamen ĝi ne sendos ĝin antaŭ vi konektos.
-
+
Mesaĝo envicigita.
-
+
Via "Ricevonto"-kampo malplenas.
-
+
Dekstre alklaku kelka(j)n elemento(j)n en via adresaro kaj elektu 'Sendi mesaĝon al tiu adreso'.
-
+
Venigis adreson de namecoin-a identigo.
-
+
Nova mesaĝo
@@ -691,47 +691,47 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
La adreso kiun vi enmetis estas malĝusta. Ignoras ĝin.
-
+
Eraro: Vi ne povas duoble aldoni la saman adreson al via adresaro. Provu renomi la ekzistan se vi volas.
-
+
Eraro: Vi ne povas aldoni duoble la saman adreson al viaj abonoj. Eble renomi la ekzistan se vi volas.
-
+
Restartigi
-
+
Vi devas restartigi Bitmesaĝon por ke la ŝanĝo de la numero de pordo (Port Number) efektivigu.
-
+
Bitmesaĝo uzos retperanton (proxy) ekde nun, sed eble vi volas permane restartigi Bitmesaĝon nun, por ke ĝi fermu eblajn ekzistajn konektojn.
-
+
Numero bezonata
-
+
Maksimumaj elŝutrapido kaj alŝutrapido devas esti numeroj. Ignoras kion vi enmetis.
-
+
Resendos neniam
-
+
Rigardu, ke la templimon vi enmetis estas pli malgrandan ol tempo dum kiu Bitmesaĝo atendas por resendi unuafoje, do viaj mesaĝoj estos senditaj neniam.
@@ -766,22 +766,22 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
Vi ja vere bezonas pasfrazon.
-
+
Adreso foriris
-
+
Bitmesaĝo ne povas trovi vian adreson %1. Ĉu eble vi forviŝis ĝin?
-
+
Adreso malŝaltita
-
+
Eraro: La adreso kun kiu vi provas sendi estas malŝaltita. Vi devos ĝin ŝalti en la langeto 'Viaj identigoj' antaŭ uzi ĝin.
@@ -791,42 +791,42 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
-
+
Aldonis elementon al la nigra listo. Redaktu la etikedon laŭvole.
-
+
Eraro: Vi ne povas duoble aldoni la saman adreson al via nigra listo. Provu renomi la jaman se vi volas.
-
+
Movis elementojn al rubujo.
-
+
Malforigis elementon.
-
+
Konservi kiel…
-
+
Skriberaro.
-
+
Neniu adreso elektita.
-
+
@@ -835,7 +835,7 @@ Are you sure you want to delete the subscription?
Ĉu vi certe volas forigi la abonon?
-
+
@@ -844,32 +844,32 @@ Are you sure you want to delete the channel?
Ĉu vi certe volas forigi la kanalon?
-
+
Ĉu vi certe volas forviŝi tiun ĉi avataron?
-
+
Vi jam agordis avataron por tiu ĉi adreso. Ĉu vi vere volas superskribi ĝin?
-
+
Starto-dum-ensaluto ne estas ankoraŭ ebla en via operaciumo.
-
+
Plejetigo al taskopleto ne estas ankoraŭ ebla en via operaciumo.
-
+
Taskopletaj sciigoj ne estas ankoraŭ eblaj en via operaciumo.
-
+
Testado…
@@ -934,192 +934,192 @@ Are you sure you want to delete the channel?
-
+
Bitmesaĝo
-
+
Identigoj
-
+
Nova identigo
-
+
Serĉi
-
+
Ĉio
-
+
Al
-
+
De
-
+
Temo
-
+
Mesaĝo
-
+
Ricevita je
-
+
Mesaĝoj
-
+
Etikedo
-
+
Adreso
-
+
Aldoni kontakton
-
+
Venigi Namecoin ID
-
+
Temo:
-
+
De:
-
+
Al:
-
+
Sendi ordinaran mesaĝon
-
+
Sendi mesaĝon al viaj abonantoj
-
+
Vivdaŭro:
-
+
Abonoj
-
+
Aldoni novan abonon
-
+
Kanaloj
-
+
Aldoni kanalon
-
+
Dosiero
-
+
Agordoj
-
+
Helpo
-
+
Enporti ŝlosilojn
-
+
Administri ŝlosilojn
-
+
Stir+Q
-
+
F1
-
+
Peti pri helpo
-
+
Pri
-
+
Regeneri antaŭkalkuleblan adreson
-
+
Forviŝi ĉiujn mesaĝojn el rubujo
-
+
- Anigi / krei kanalon
+ Aniĝi / Krei kanalon
-
+
Ĉiuj kontoj
@@ -1139,67 +1139,67 @@ Are you sure you want to delete the channel?
Aldoni novan elementon
-
+
-
+ Montri %1 lasta(j)n elsendo(j)n de tiu ĉi adreso.
-
+
La nova versio de PyBitmessage estas disponebla: %1. Elŝutu ĝin de https://github.com/Bitmessage/PyBitmessage/releases/latest
-
+
Atendado ĝis laborpruvo finiĝos… %1%
-
+
Fermado de PyBitmessage… %1%
-
+
Atendado ĝis objektoj estos senditaj… %1%
-
+
Konservado de agordoj… %1%
-
+
Fermado de kerno… %1%
-
+
Haltigado de sciigoj… %1%
-
+
Fermado tuj… %1%
-
+
%n horo%n horoj
-
+
%n tago%n tagoj
-
+
Fermado de PyBitmessage… %1%
-
+
Senditaj
@@ -1303,7 +1303,7 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2
Eraro: Vi provis sendi mesaĝon al vi mem aŭ al kanalo, tamen via ĉifroŝlosilo ne estas trovebla en la dosiero keys.dat. Mesaĝo ne povis esti ĉifrita. %1
-
+
Kalkulado de laborpruvo, kiu endas por sendi mesaĝon.
@@ -1313,7 +1313,7 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2
Mesaĝo sendita. Atendado je konfirmo. Sendita je %1
-
+
Kalkulado de laborpruvo, kiu endas por peti pri ĉifroŝlosilo.
@@ -1338,37 +1338,37 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2
UPnP pord-mapigo forigita
-
+
Marki ĉiujn mesaĝojn kiel legitajn
-
+
Ĉu vi certe volas marki ĉiujn mesaĝojn kiel legitajn?
-
+
Kalkulado de laborpruvo, kiu endas por sendi elsendon.
-
+
Laborpruvo haltigita
-
+
Haltigis laborpruvon por %n objektoHaltigis laborpruvon por %n objektoj
-
+
%n objekto atendas je sendato%n objektoj atendas je sendato
-
+
Ĉu atendi ĝis tiujn taskojn finos?
@@ -1413,37 +1413,32 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2
La nomo %1 ne estas atribuita kun bitmesaĝa adreso.
-
+
Sukceso! Namecoind versio %1 funkcias.
-
+
Sukceso! NMControl funkcias ĝuste.
-
+
Ne povis kompreni NMControl.
-
-
-
- Malsukcesis konekti al namecoin.
-
Via(j) vidprocesoro(j) ne kalkulis senerare, malaktiviganta OpenCL. Bonvolu raporti tion al programistoj.
-
+
Agordi sciigan sonon…
-
+
-
+
malkonsilinda por kanaloj
-
+
Silenta reĝimo
-
+
Ĉu problemo kun konektado? Provu aktivigi UPnP en retaj agordoj.
-
+
Vi provas sendi retmesaĝon anstataŭ bitmesaĝ-mesaĝon. Tio ĉi postulas registri ĉe retpoŝta kluzo. Ĉu provi registri?
-
+
Eraro: bitmesaĝaj adresoj komenciĝas kun BM-. Bonvolu kontroli la adreson de ricevonto %1
-
+
Eraro: la adreso de ricevonto %1 estas malprave tajpita aŭ kopiita. Bonvolu kontroli ĝin.
-
+
Eraro: la adreso de ricevonto %1 enhavas malpermesatajn simbolojn. Bonvolu kontroli ĝin.
-
+
Eraro: la versio de adreso de ricevonto %1 estas tro alta. Eble vi devas ĝisdatigi vian bitmesaĝan programon aŭ via sagaca konato uzas alian programon.
-
+
Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro mallongaj. Povus esti ke io en la programo de via konato malfunkcias.
-
+
Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro longaj. Povus esti ke io en la programo de via konato malfunkcias.
-
+
Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas misformitaj. Povus esti ke io en la programo de via konato malfunkcias.
-
+
Eraro: io malĝustas kun la adreso de ricevonto %1.
-
+
Eraro: %1
-
+
De %1
-
+
Samtempigado haltigita
-
+
Bitmesaĝo ne estas samtempigita kun la reto, %n objekto elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos?Bitmesaĝo ne estas samtempigita kun la reto, %n objektoj elŝutendas. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis la samtempigado finiĝos?
-
+
Nekonektita
-
+
Bitmesaĝo ne estas konektita al la reto. Se vi eliros nun, tio povas igi malfruiĝojn de liveradoj. Ĉu atendi ĝis ĝi konektos kaj la samtempigado finiĝos?
-
+
Atendado je retkonekto…
-
+
Atendado ĝis samtempigado finiĝos…
-
+
Vi jam agordis sciigan sonon por tiu ĉi adreso. Ĉu vi volas anstataŭigi ĝin?
-
-
- Eraro okazis: ne povis legi mesaĝon el la disko.
-
-
-
-
- Montri %n lastan elsendon de tiu ĉi adreso.Montri %n lastajn elsendojn de tiu ĉi adreso.
-
-
-
+
Konekti
-
+
Malkonekti
-
-
-
- Forviŝi
-
-
-
-
- Ricevujo
-
-
-
-
- novaj
-
-
-
-
- senditaj
-
-
-
-
- rubujo
-
MessageView
@@ -1633,14 +1593,14 @@ Bonvenon al facila kaj sekura Bitmesaĝo
MsgDecode
-
+
La mesaĝo enhavas nekonatan kodoprezenton.
Eble vi devas ĝisdatigi Bitmesaĝon.
-
+
Nekonata kodoprezento
@@ -1796,7 +1756,7 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
Nomo de kvazaŭ-dissendlisto:
-
+
Tio ĉi estas kanaladreso. Vi ne povas ĝin uzi kiel kvazaŭ-dissendolisto.
@@ -1819,12 +1779,12 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
-
+
<html><head/><body><p>Distribuata laŭ la permesilo “MIT/X11 software license”; legu <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html>
-
+
Tio ĉi estas beta-eldono.
@@ -1834,7 +1794,7 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
-
+
<html><head/><body><p>Kopirajto © 2012-2016 Jonathan WARREN<br/>Kopirajto © 2013-2017 La Programistoj de Bitmesaĝo</p></body></html>
@@ -2004,27 +1964,27 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
-
+
Ekde lanĉo de la programo je %1
-
+
Elŝuto: %1/s Sume: %2
-
+
Alŝuto: %1/s Sume: %2
-
+
Ĉiuj konektoj: %1
-
+
Petoj pri inventaro en sekundo: %1
@@ -2039,32 +1999,32 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
Elŝuto: 0 kB/s
-
+
- Reto
+ Reta stato
-
+
bitokobitokoj
-
+
Objekto por samtempigi: %nObjektoj por samtempigi: %n
-
+
Pritraktis %n inter-personan mesaĝon.Pritraktis %n inter-personajn mesaĝojn.
-
+
Pritraktis %n elsendon.Pritraktis %n elsendojn.
-
+
Pritraktis %n publikan ŝlosilon.Pritraktis %n publikajn ŝlosilojn.
@@ -2159,7 +2119,7 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
- Krei aŭ anigi kanalon
+ Krei aŭ aniĝi kanalon
@@ -2192,7 +2152,7 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
- Sukcese kreis / anigis al la kanalo %1
+ Sukcese kreis / aniĝis al la kanalo %1
@@ -2450,7 +2410,7 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
- Agordoj de reto
+ Retaj agordoj
diff --git a/src/translations/bitmessage_fr.qm b/src/translations/bitmessage_fr.qm
index e544a1c3..742e62d8 100644
Binary files a/src/translations/bitmessage_fr.qm and b/src/translations/bitmessage_fr.qm differ
diff --git a/src/translations/bitmessage_fr.ts b/src/translations/bitmessage_fr.ts
index becfcbf8..3e64d657 100644
--- a/src/translations/bitmessage_fr.ts
+++ b/src/translations/bitmessage_fr.ts
@@ -2,17 +2,17 @@
AddAddressDialog
-
+
Ajouter une nouvelle entrée
-
+
Étiquette
-
+
Adresse
@@ -20,99 +20,70 @@
EmailGatewayDialog
-
+
Passerelle de courriel
-
+
S’inscrire sur la passerelle de courriel
-
+
Statut du compte sur la passerelle de courriel
-
+
Changer les paramètres de compte sur la passerelle de courriel
-
+
Se désabonner de la passerelle de courriel
-
+
La passerelle de courriel vous permet de communiquer avec des utilisateurs de courriel. Actuellement, seulement la passerelle de courriel de Mailchuck (@mailchuck.com) est disponible.
-
+
Adresse de courriel désirée (incluant @mailchuck.com) :
-
-
-
- @mailchuck.com
-
-
-
-
- L’inscription a échoué :
-
-
-
-
- L'adresse électronique demandée n'est pas disponible, veuillez essayer une nouvelle.
-
-
-
-
- Envoi de la demande d’inscription de la passerelle de courriel
-
-
-
-
- Envoi de la demande de désinscription de la passerelle de courriel
-
-
-
-
- Envoi à la passerelle de courriel d’une demande de statut
-
EmailGatewayRegistrationDialog
-
+
-
+ L’inscription a échoué :
-
+
-
+ L’adresse de courriel demandée n’est pas disponible, veuillez en essayer une nouvelle. Saisissez ci-dessous la nouvelle adresse de courriel désirée (incluant @mailchuck.com) :
-
+ Inscription à la passerelle de courriel
-
+ La passerelle de courriel vous permet de communiquer avec des utilisateurs de courriels. Actuellement, seule la passerelle de courriel de Mailchuck (@mailchuck.com) est disponible.
+Veuillez taper l’adresse de courriel désirée (incluant @mailchuck.com) :
Mailchuck
-
+
MainWindow
-
+
Répondre à l’expéditeur
-
+
Répondre au canal
-
+
Ajouter l’expéditeur au carnet d’adresses
-
+
Ajouter l’expéditeur à votre liste noire
-
+
Envoyer à la Corbeille
-
+
Restaurer
-
+
Voir le code HTML comme du texte formaté
-
+
Enregistrer le message sous…
-
+
Marquer comme non-lu
-
+
Nouvelle
-
+
Activer
-
+
Désactiver
-
+
Configurer l’avatar
-
+
Copier l’adresse dans le presse-papier
-
+
Comportement spécial de l’adresse…
-
+
Passerelle de courriel
-
+
Effacer
-
+
Envoyer un message à cette adresse
-
+
S’abonner à cette adresse
-
+
Ajouter une nouvelle adresse
-
+
Copier l’adresse de destination dans le presse-papier
-
+
Forcer l’envoi
-
+
Une de vos adresses, %1, est une vieille adresse de la version 1. Les adresses de la version 1 ne sont plus supportées. Nous pourrions la supprimer maintenant?
-
+
En attente de la clé de chiffrement. Une nouvelle requête sera bientôt lancée.
@@ -327,17 +298,17 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
En attente.
-
+
Message envoyé. En attente de l’accusé de réception. Envoyé %1
-
+
Message envoyé. Envoyé %1
@@ -347,77 +318,77 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
Accusé de réception reçu %1
-
+
Message de diffusion en attente.
-
+
Message de diffusion du %1
-
+
Problème : Le travail demandé par le destinataire est plus difficile que ce que vous avez paramétré. %1
-
+
Problème : la clé de chiffrement du destinataire n’est pas bonne. Il n’a pas été possible de chiffrer le message. %1
-
+
Neutralisation forcée de la difficulté. L’envoi devrait bientôt commencer.
-
+
Statut inconnu : %1 %2
-
+
Déconnecté
-
+
Afficher Bitmessage
-
+
Envoyer
-
+
S’abonner
-
+
Canal
-
+
Quitter
-
+
Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le même répertoire que ce programme. Il est important de faire des sauvegardes de ce fichier.
-
+
@@ -425,54 +396,54 @@ It is important that you back up this file.
Il est important de faire des sauvegardes de ce fichier.
-
+
Ouvrir keys.dat ?
-
+
Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le même répertoire que ce programme. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant ? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.)
-
+
Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le répertoire %1. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.)
-
+
Supprimer la corbeille ?
-
+
Êtes-vous sûr de vouloir supprimer tous les messages dans la corbeille ?
-
+
Mauvaise phrase secrète
-
+
Vous devez taper votre phrase secrète. Si vous n’en avez pas, ce formulaire n’est pas pour vous.
-
+
Mauvais numéro de version d’adresse
-
+
Votre numéro de version d’adresse doit être un nombre : soit 3 soit 4.
-
+
Votre numéro de version d’adresse doit être soit 3 soit 4.
@@ -542,22 +513,22 @@ It is important that you back up this file. Would you like to open the file now?
-
+
Connexion perdue
-
+
Connecté
-
+
Message envoyé à la corbeille
-
+
-
+
Message trop long
-
+
Le message que vous essayez d’envoyer est trop long de %1 octets (le maximum est 261644 octets). Veuillez le réduire avant de l’envoyer.
-
+
Erreur : votre compte n’a pas été inscrit à une passerelle de courrier électronique. Envoi de l’inscription maintenant en tant que %1, veuillez patienter tandis que l’inscription est en cours de traitement, avant de retenter l’envoi.
@@ -621,57 +592,57 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r
-
+
Erreur : Vous devez spécifier une adresse d’expéditeur. Si vous n’en avez pas, rendez-vous dans l’onglet 'Vos identités'.
-
+
Numéro de version de l’adresse
-
+
Concernant l’adresse %1, Bitmessage ne peut pas comprendre les numéros de version de %2. Essayez de mettre à jour Bitmessage vers la dernière version.
-
+
Numéro de flux
-
+
Concernant l’adresse %1, Bitmessage ne peut pas supporter les nombres de flux de %2. Essayez de mettre à jour Bitmessage vers la dernière version.
-
+
Avertissement : Vous êtes actuellement déconnecté. Bitmessage fera le travail nécessaire pour envoyer le message mais il ne sera pas envoyé tant que vous ne vous connecterez pas.
-
+
Message mis en file d’attente.
-
+
Votre champ 'Vers' est vide.
-
+
Cliquez droit sur une ou plusieurs entrées dans votre carnet d’adresses et sélectionnez 'Envoyer un message à ces adresses'.
-
+
Récupération avec succès de l’adresse de l’identité Namecoin.
-
+
Nouveau message
@@ -681,157 +652,157 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r
-
+
-
+ Envoi de la demande d’inscription de la passerelle de courriel
-
+
L’adresse est valide.
-
+
L’adresse que vous avez entrée est invalide. Adresse ignorée.
-
+
Erreur : Vous ne pouvez pas ajouter une adresse déjà présente dans votre carnet d’adresses. Essayez de renommer l’adresse existante.
-
+
Erreur : vous ne pouvez pas ajouter la même adresse deux fois à vos abonnements. Peut-être que vous pouvez renommer celle qui existe si vous le souhaitez.
-
+
Redémarrer
-
+
Vous devez redémarrer Bitmessage pour que le changement de port prenne effet.
-
+
Bitmessage utilisera votre proxy dorénavant, mais vous pouvez redémarrer manuellement Bitmessage maintenant afin de fermer des connexions existantes (si il y en existe).
-
+
Nombre requis
-
+
Vos taux maximum de téléchargement et de téléversement doivent être des nombres. Ce que vous avez tapé est ignoré.
-
+
Ne renverra jamais
-
+
Notez que la limite de temps que vous avez entrée est plus courte que le temps d’attente respecté par Bitmessage avant le premier essai de renvoi, par conséquent votre message ne sera jamais renvoyé.
-
+
-
+ Envoi de la demande de désinscription de la passerelle de courriel
-
+
-
+ Envoi à la passerelle de courriel d’une demande de statut
-
+
Phrases secrètes différentes
-
+
Les phrases secrètes entrées sont différentes. Réessayez.
-
+
Choisissez une phrase secrète
-
+
Vous devez vraiment utiliser une phrase secrète.
-
+
L’adresse a disparu
-
+
Bitmessage ne peut pas trouver votre adresse %1. Peut-être l’avez-vous supprimée?
-
+
Adresse désactivée
-
+
Erreur : L’adresse avec laquelle vous essayez de communiquer est désactivée. Vous devez d’abord l’activer dans l’onglet 'Vos identités' avant de l’utiliser.
-
+
-
+ Entrée ajoutée au carnet d’adresse. Éditez l’étiquette à votre convenance.
-
+
Entrée ajoutée à la liste noire. Éditez l’étiquette à votre convenance.
-
+
Erreur : vous ne pouvez pas ajouter la même adresse deux fois à votre liste noire. Essayez de renommer celle qui existe si vous le souhaitez.
-
+
Messages déplacés dans la corbeille.
-
+
Articles restaurés.
-
+
Enregistrer sous…
-
+
Erreur d’écriture.
-
+
Aucune adresse sélectionnée.
-
+
@@ -840,7 +811,7 @@ Are you sure you want to delete the subscription?
Êtes-vous sur de vouloir supprimer cet abonnement ?
-
+
@@ -849,282 +820,282 @@ Are you sure you want to delete the channel?
Êtes-vous sûr de vouloir supprimer ce canal ?
-
+
Voulez-vous vraiment enlever cet avatar ?
-
+
Vous avez déjà mis un avatar pour cette adresse. Voulez-vous vraiment l’écraser ?
-
+
Le démarrage dès l’ouverture de session n’est pas encore supporté sur votre OS.
-
+
La minimisation en zone système n’est pas encore supportée sur votre OS.
-
+
Les notifications en zone système ne sont pas encore supportées sur votre OS.
-
+
Tester…
-
+
-
+ Ceci est une adresse de canal. Vous ne pouvez pas l’utiliser en tant que pseudo liste de diffusion.
-
+
L’adresse devrait commencer avec "BM-"
-
+
L’adresse n’est pas correcte (la somme de contrôle a échoué).
-
+
Le numéro de version de cette adresse est supérieur à celui que le programme peut supporter. Veuiller mettre Bitmessage à jour.
-
+
L’adresse contient des caractères invalides.
-
+
Certaines données encodées dans l’adresse sont trop courtes.
-
+
Certaines données encodées dans l’adresse sont trop longues.
-
+
Quelques données codées dans l’adresse sont mal formées.
-
+
-
+ Entrez ci-dessus une adresse.
-
+
L’adresse est d’ancien type. Nous ne pouvons pas montrer ses messages de diffusion passés.
-
+
Il n’y a aucun message de diffusion récent de cette adresse à afficher.
-
+
-
+ Vous utilisez le port TCP %1. (Ceci peut être changé dans les paramètres).
-
+
Bitmessage
-
+
Identités
-
+
Nouvelle identité
-
+
Chercher
-
+
Tous
-
+
Vers
-
+
De
-
+
Sujet
-
+
Message
-
+
Reçu
-
+
Messages
-
+
Carnet d’adresses
-
+
Adresse
-
+
Ajouter un contact
-
+
Récupère l’ID Namecoin
-
+
Sujet :
-
+
De :
-
+
Vers :
-
+
Envoyer un message ordinaire
-
+
Envoyer un message à vos abonnés
-
+
TTL :
-
+
Abonnements
-
+
Ajouter un nouvel abonnement
-
+
Canaux
-
+
Ajouter un canal
-
+
Fichier
-
+
Paramètres
-
+
Aide
-
+
Importer les clés
-
+
Gérer les clés
-
+
Ctrl+Q
-
+
F1
-
+
Contacter le support
-
+
À propos
-
+
Regénérer les clés déterministes
-
+
Supprimer tous les messages dans la corbeille
-
+
Rejoindre / créer un canal
-
+
Tous les comptes
@@ -1134,77 +1105,77 @@ Are you sure you want to delete the channel?
Niveau de zoom %1%
-
+
Erreur : vous ne pouvez pas ajouter la même adresse deux fois à votre liste. Vous pouvez peut-être, si vous le souhaitez, renommer celle qui existe déjà.
-
+
Ajouter une nouvelle entrée
-
+
-
+ Montre le(s) %1 plus récent(s) message(s) de diffusion issu(s) de cette adresse.
-
+
Une nouvelle version de PyBitmessage est disponible : %1. Veuillez la télécharger depuis https://github.com/Bitmessage/PyBitmessage/releases/latest
-
+
En attente de la fin de la PoW… %1%
-
+
Pybitmessage en cours d’arrêt… %1%
-
+
En attente de l’envoi des objets… %1%
-
+
Enregistrement des paramètres… %1%
-
+
Cœur en cours d’arrêt… %1%
-
+
Arrêt des notifications… %1%
-
+
Arrêt imminent… %1%
-
+
%n heure%n heures
-
+
%n jour%n jours
-
+
PyBitmessage en cours d’arrêt… %1%
-
+
Envoyé
@@ -1249,86 +1220,86 @@ Are you sure you want to delete the channel?
Alerte : votre disque ou le volume de stockage de données est plein. Bitmessage va maintenant se fermer.
-
+
Erreur ! Il n’a pas été possible de trouver l’adresse d’expéditeur (votre adresse) dans le fichier keys.dat.
-
+
Travail en cours afin d’envoyer le message de diffusion…
-
+
Message de diffusion envoyé %1
-
+
La clé de chiffrement a été demandée plus tôt.
-
+
Envoi d’une demande de la clé de chiffrement du destinataire.
-
+
Recherche de la clé publique du récepteur
-
+
Problème : la destination est un dispositif mobile qui nécessite que la destination soit incluse dans le message mais ceci n’est pas autorisé dans vos paramètres. %1
-
+
Travail en cours afin d’envoyer le message.
Il n’y a pas de difficulté requise pour les adresses version 2 comme celle-ci.
-
+
Travail en cours afin d’envoyer le message.
Difficulté requise du destinataire : %1 et %2
-
+
Problème : Le travail demandé par le destinataire (%1 and %2) est plus difficile que ce que vous avez paramétré. %3
-
+
Problème : Vous essayez d’envoyer un message à un canal ou à vous-même mais votre clef de chiffrement n’a pas été trouvée dans le fichier keys.dat. Le message ne peut pas être chiffré. %1
-
+
Travail en cours afin d’envoyer le message.
-
+
Message envoyé. En attente de l’accusé de réception. Envoyé %1
-
+
Travail en cours afin d’obtenir la clé de chiffrement.
-
+
Diffusion de la demande de clef publique. Ce programme réessaiera automatiquement si ils sont déconnectés.
-
+
Envoi d’une demande de clef publique. En attente d’une réponse. Demandée à %1
@@ -1343,37 +1314,37 @@ Difficulté requise du destinataire : %1 et %2
Transfert de port UPnP retiré
-
+
Marquer tous les messages comme lus
-
+
Êtes-vous sûr(e) de vouloir marquer tous les messages comme lus ?
-
+
Travail en cours afin d’envoyer la diffusion.
-
+
En attente de preuve de fonctionnement
-
+
%n objet en attente de preuve de fonctionnement%n objet(s) en attente de preuve de fonctionnement
-
+
%n objet en attente d'être distribué%n objet(s) en attente d'être distribués
-
+
Attendre jusqu'à ce que ces tâches se terminent ?
@@ -1418,37 +1389,32 @@ Difficulté requise du destinataire : %1 et %2
Le nom %1 n'a aucune adresse Bitmessage d'associée.
-
+
Succès ! Namecoind version %1 en cours d'exécution.
-
+
Succès ! NMControll est debout et en cours d'exécution.
-
+
Ne pouvait pas comprendre NMControl.
-
-
-
- La connexion à Namecoin a échouée.
-
Votre GPU(s) n'a pas calculé correctement, mettant OpenCL hors service. Veuillez remonter ceci aux développeurs s'il vous plaît.
-
+
Mettre un son de notification ...
-
+
-
+
pas recommandé pour les canaux
-
-
- Mode tranquille
-
-
-
+
Des difficultés à se connecter ? Essayez de permettre UPnP dans les "Paramètres réseau"
-
+
Vous essayez d'envoyer un courrier électronique au lieu d'un bitmessage. Ceci exige votre inscription à une passerelle. Essayer de vous inscrire ?
-
+
Erreur : Les adresses Bitmessage commencent par BM- Veuillez vérifier l'adresse du destinataire %1
-
+
Erreur : L’adresse du destinataire %1 n’est pas correctement tapée ou recopiée. Veuillez la vérifier.
-
+
Erreur : L’adresse du destinataire %1 contient des caractères invalides. Veuillez la vérifier.
-
+
Erreur : la version de l’adresse destinataire %1 est trop élevée. Vous devez mettre à niveau votre logiciel Bitmessage ou alors celui de votre connaissance est plus intelligent.
-
+
Erreur : quelques données codées dans l’adresse destinataire %1 sont trop courtes. Il pourrait y avoir un soucis avec le logiciel de votre connaissance.
-
+
Erreur : quelques données codées dans l’adresse destinataire %1 sont trop longues. Il pourrait y avoir un soucis avec le logiciel de votre connaissance.
-
+
Erreur : quelques données codées dans l’adresse destinataire %1 sont mal formées. Il pourrait y avoir un soucis avec le logiciel de votre connaissance.
-
+
Erreur : quelque chose ne va pas avec l'adresse de destinataire %1.
-
-
- Erreur : %1
-
-
-
+
De %1
-
+
En attente de synchronisation
-
+
Bitmessage ne s'est pas synchronisé avec le réseau, %n objet(s) à télécharger. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce que la synchronisation aboutisse ?Bitmessage ne s'est pas synchronisé avec le réseau, %n objet(s) à télécharger. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce que la synchronisation aboutisse ?
-
+
Non connecté
-
+
Bitmessage n'est pas connecté au réseau. Si vous quittez maintenant, cela pourrait causer des retards de livraison. Attendre jusqu'à ce qu'il soit connecté et que la synchronisation se termine ?
-
+
En attente de connexion réseau...
-
+
En attente d'achèvement de la synchronisation...
-
+
Vous avez déjà mis un son de notification pour cette adresse. Voulez-vous vraiment l’écraser ?
-
-
-
- Une erreur a eu lieu : ne peut pas charger de message depuis le disque.
-
-
-
-
-
-
-
-
-
- Passer en-ligne
-
-
-
-
- Passer hors-ligne
-
-
-
-
- Vider
-
-
-
-
- entrant
-
-
-
-
- nouveau
-
-
-
-
- envoyé
-
-
-
-
- corbeille
-
MessageView
-
+
Suivre lien externe
-
+
Le lien "%1" s'ouvrira dans un navigateur. Cela pourrait être un risque de sécurité, cela pourrait vous désanonymiser ou télécharger des données malveillantes. Êtes-vous sûr(e) ?
-
+
HTML détecté, cliquer ici pour l'afficher
-
+
Cliquer ici pour mettre hors de service le HTML
@@ -1639,14 +1550,14 @@ Bienvenue dans le facile et sécurisé Bitmessage
MsgDecode
-
+
Le message est codé de façon inconnue.
Peut-être que vous devriez mettre à niveau Bitmessage.
-
+
Encodage inconnu
@@ -1654,98 +1565,98 @@ Peut-être que vous devriez mettre à niveau Bitmessage.
NewAddressDialog
-
+
Créer une nouvelle adresse
-
+
Vous pouvez générer autant d’adresses que vous le souhaitez. En effet, nous vous encourageons à créer et à délaisser vos adresses. Vous pouvez générer des adresses en utilisant des nombres aléatoires ou en utilisant une phrase secrète. Si vous utilisez une phrase secrète, l’adresse sera une adresse "déterministe". L’option 'Nombre Aléatoire' est sélectionnée par défaut mais les adresses déterministes ont certains avantages et inconvénients :
-
+
<html><head/><body><p><span style=" font-weight:600;">Avantages :<br/></span>Vous pouvez recréer vos adresses sur n’importe quel ordinateur. <br/>Vous n’avez pas à vous inquiéter à propos de la sauvegarde de votre fichier keys.dat tant que vous vous rappelez de votre phrase secrète. <br/><span style=" font-weight:600;">Inconvénients :<br/></span>Vous devez vous rappeler (ou noter) votre phrase secrète si vous souhaitez être capable de récréer vos clés si vous les perdez. <br/>Vous devez vous rappeler du numéro de version de l’adresse et du numéro de flux en plus de votre phrase secrète. <br/>Si vous choisissez une phrase secrète faible et que quelqu’un sur Internet parvient à la brute-forcer, il pourra lire vos messages et vous en envoyer.</p></body></html>
-
+
Utiliser un générateur de nombres aléatoires pour créer une adresse
-
+
Utiliser une phrase secrète pour créer une adresse
-
+
Créer une adresse plus courte d’un ou deux caractères (nécessite plusieurs minutes de temps de calcul supplémentaires)
-
+
Créer une adresse déterministe
-
+
Numéro de version de l’adresse : 4
-
+
En plus de votre phrase secrète, vous devez vous rappeler ces numéros:
-
+
Phrase secrète
-
+
- Nombre d’adresses basées sur votre phrase secrète à créer :
+ Nombre d’adresses à créer sur base de votre phrase secrète:
-
+
Nombre de flux : 1
-
+
Retapez la phrase secrète
-
+
Générer une adresse de manière aléatoire
-
+
Étiquette (seulement visible par vous)
-
+
Utiliser le flux le plus disponible
-
+
(préférable si vous générez votre première adresse)
-
+
Utiliser le même flux qu’une adresse existante
-
+
(économise de la bande passante et de la puissance de calcul)
@@ -1753,22 +1664,22 @@ The 'Random Number' option is selected by default but deterministic ad
NewSubscriptionDialog
-
+
Ajouter une nouvelle entrée
-
+
Étiquette
-
+
Adresse
-
+
Entrez ci-dessus une adresse.
@@ -1776,60 +1687,55 @@ The 'Random Number' option is selected by default but deterministic ad
SpecialAddressBehaviorDialog
-
+
Comportement spécial de l’adresse
-
+
Se comporter comme une adresse normale
-
+
Se comporter comme une adresse d’une pseudo liste de diffusion
-
+
Un mail reçu sur une adresse d’une pseudo liste de diffusion sera automatiquement diffusé aux abonnés (et sera donc public).
-
+
Nom de la pseudo liste de diffusion :
-
-
-
- Ceci est une adresse de canal. Vous ne pouvez pas l’utiliser en tant que pseudo liste de diffusion.
-
aboutDialog
-
+
À propos
-
-
-
-
-
-
-
+
+ PyBitmessage
-
+
+
+ version ?
+
+
+
<html><head/><body><p>Distribué sous la licence logicielle MIT/X11; voir <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html>
-
+
Version bêta.
@@ -1838,10 +1744,10 @@ The 'Random Number' option is selected by default but deterministic ad
-
-
-
- <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 Les développeurs de Bitmessage</p></body></html>
+
+
+
+ <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2016 Les développeurs de Bitmessage</p></body></html>
@@ -1872,12 +1778,12 @@ The 'Random Number' option is selected by default but deterministic ad
Adresse
-
+
Liste noire
-
+
Liste blanche
@@ -1885,45 +1791,40 @@ The 'Random Number' option is selected by default but deterministic ad
connectDialog
-
+
Bitmessage
-
+
Bitmessage ne connectera à personne avant que vous ne le laissiez faire.
-
+
Connexion maintenant
-
+
Me laisser d’abord configurer des paramètres spéciaux de réseau
-
-
-
- Travailler hors-ligne
-
helpDialog
-
+
Aide
-
+
<a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a>
-
+
Bitmessage étant un projet collaboratif, une aide peut être trouvée en ligne sur le Wiki de Bitmessage:
@@ -1931,35 +1832,30 @@ The 'Random Number' option is selected by default but deterministic ad
iconGlossaryDialog
-
+
Glossaire des icônes
-
+
Vous n’avez aucune connexion avec d’autres pairs.
-
+
Vous avez au moins une connexion sortante avec un pair mais vous n’avez encore reçu aucune connexion entrante. Votre pare-feu ou routeur n’est probablement pas configuré pour transmettre les connexions TCP vers votre ordinateur. Bitmessage fonctionnera correctement, mais le réseau Bitmessage se portera mieux si vous autorisez les connexions entrantes. Cela vous permettra d’être un nœud mieux connecté.
-
+ Vous utilisez le port TCP ?. (Peut être changé dans les paramètres).
-
+
Vous avez des connexions avec d’autres pairs et votre pare-feu est configuré correctement.
-
-
-
- Vous utilisez le port TCP %1. (Ceci peut être changé dans les paramètres).
-
networkstatus
@@ -1969,37 +1865,37 @@ The 'Random Number' option is selected by default but deterministic ad
Total de connexions:
-
+
Depuis le démarrage :
-
+
Traité 0 messages de personne à personne.
-
+
Traité 0 clés publiques.
-
+
Traité 0 message de diffusion.
-
+
Consultations d’inventaire par seconde : 0
-
+
Objets à synchroniser :
-
+
Flux N°
@@ -2009,114 +1905,114 @@ The 'Random Number' option is selected by default but deterministic ad
-
+
Démarré depuis le %1
-
+
Téléchargées : %1/s Total : %2
-
+
Téléversées : %1/s Total : %2
-
+
Total des connexions : %1
-
+
Consultations d’inventaire par seconde : %1
-
+
Téléversement : 0 kO/s
-
+
Téléchargement : 0 kO/s
-
+
Statut du réseau
-
+
octetoctets
-
+
Objet à synchroniser : %nObjets à synchroniser : %n
-
+
Traité %n message de personne à personne.Traité %n messages de personne à personne.
-
+
Traité %n message de diffusion.Traité %n messages de diffusion.
-
+
Traité %n clé publique.Traité %n clés publiques.
-
+
Pair
-
+
Adresse IP ou nom d'hôte
-
+
Évaluation
-
+
PyBitmessage suit à la trace le taux de réussites de connexions tentées vers les noeuds individuels. L'évaluation s'étend de -1 à 1 et affecte la probabilité de choisir ce noeud dans l'avenir
-
+
Agent utilisateur
-
+
Logiciel, auto-rapporté par le pair
-
+
TLS
-
+
- Chiffrement de la connexion
+
-
+
- Liste de flux négociés entre vous et le pair
+
@@ -2173,8 +2069,8 @@ The 'Random Number' option is selected by default but deterministic ad
-
-
+
+ Nom ou phrase mot de passe du canal :
@@ -2195,7 +2091,7 @@ The 'Random Number' option is selected by default but deterministic ad
newchandialog
-
+
Le canal %1 a été rejoint ou créé avec succès.
@@ -2239,52 +2135,52 @@ The 'Random Number' option is selected by default but deterministic ad
regenerateAddressesDialog
-
+
Regénérer des adresses existantes
-
+
- Adresses existantes régénérées
+ Regénérer des adresses existantes
-
+
Phrase secrète
-
+
Nombre d’adresses basées sur votre phrase secrète à créer :
-
+
Numéro de version de l’adresse :
-
+
Numéro du flux :
-
+
1
-
+
Créer une adresse plus courte d’un ou deux caractères (nécessite plusieurs minutes de temps de calcul supplémentaires)
-
+
Vous devez cocher (ou décocher) cette case comme vous l’aviez fait (ou non) lors de la création de vos adresses la première fois.
-
+
Si vous aviez généré des adresses déterministes mais les avez perdues à cause d’un accident (comme une panne de disque dur), vous pouvez les régénérer ici. Si vous aviez utilisé le générateur de nombres aléatoires pour créer vos adresses, ce formulaire ne vous sera d’aucune utilité.
diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm
index 9135be1a..3756d6d3 100644
Binary files a/src/translations/bitmessage_ja.qm and b/src/translations/bitmessage_ja.qm differ
diff --git a/src/translations/bitmessage_ja.ts b/src/translations/bitmessage_ja.ts
index 7f1e4515..90e49dc2 100644
--- a/src/translations/bitmessage_ja.ts
+++ b/src/translations/bitmessage_ja.ts
@@ -2,17 +2,17 @@
AddAddressDialog
-
+
新しい項目を追加
-
+
ラベル
-
+
アドレス
@@ -20,99 +20,70 @@
EmailGatewayDialog
-
+
メールゲートウェイ
-
+
メールゲートウェイで登録
-
+
メールゲートウェイのアカウント状態
-
+
メールゲートウェイでアカウントの設定を変更
-
+
メールゲートウェイから登録抹消
-
+
メールゲートウェイを使用すると、メールユーザーと通信できます。 現在、Mailchuck メールゲートウェイ (@mailchuck.com) のみが利用可能です。
-
+
希望のメールアドレス (@mailchuck.com を含む):
-
-
-
- @mailchuck.com
-
-
-
-
- 登録に失敗しました:
-
-
-
-
- リクエストしたメールアドレスは利用できません。新しいメールアドレスを試してください。
-
-
-
-
- メールゲートウェイの登録リクエストを送信しています
-
-
-
-
- メールゲートウェイの登録抹消リクエストを送信しています
-
-
-
-
- メールゲートウェイの状態リクエストを送信しています
-
EmailGatewayRegistrationDialog
-
+
-
+ 登録に失敗しました:
-
+
-
+ リクエストしたメールアドレスは利用できません。新しいメールアドレスをお試しください。 新しい希望メールアドレス (@mailchuck.com を含む) を次のように記入してください:
-
+ メールゲートウェイの登録
-
+ メールゲートウェイを使用すると、メールユーザーと通信できます。 現在、Mailchuck メールゲートウェイ (@mailchuck.com) のみが利用可能です。
+希望のメールアドレス (@mailchuck.com を含む) を以下に入力してください:
Mailchuck
-
+
MainWindow
-
+
送信元に返信
-
+
チャンネルに返信
-
+
送信元をアドレス帳に追加
-
+
送信元をブラックリストに追加
-
+
ゴミ箱へ移動
-
+
削除を元に戻す
-
+
HTMLコードを整形したテキストで表示
-
+
形式を選択してメッセージを保存
-
+
未読にする
-
+
新規
-
+
有効
-
+
無効
-
+
アバターを設定...
-
+
アドレスをコピー
-
+
アドレスの特別な動作
-
+
メールゲートウェイ
-
+
削除
-
+
このアドレスへ送信
-
+
このアドレスを購読
-
+
アドレスを追加
-
+
宛先アドレスをコピー
-
+
強制的に送信
-
+
%1は古いバージョン1のアドレスです。バージョン1のアドレスはサポートが終了しています。すぐに削除しますか?
-
+
暗号鍵を待っています。 すぐにもう一度リクエストします。
@@ -322,17 +293,17 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
キューに入りました。
-
+
メッセージを送信しました。 確認応答を待っています。 %1 で送信されました
-
+
メッセージは送信されました。送信先: %1
@@ -342,131 +313,131 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
メッセージの確認を受け取りました %1
-
+
配信がキューに入りました。
-
+
配信: %1
-
+
問題: 受信者が要求している処理は現在あなたが設定しているよりも高い難易度です。 %1
-
+
問題: 受信者の暗号鍵は正当でない物です。メッセージを暗号化できません。 %1
-
+
難易度を強制上書きしました。まもなく送信されます。
-
+
不明なステータス: %1 %2
-
+
未接続
-
+
Bitmessageを表示
-
+
送る
-
+
購読
-
+
チャンネル
-
+
終了
-
+
プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。
-
+
%1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。
-
+
keys.datを開きますか?
-
+
プログラムを同じディレクトリに保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください)
-
+
%1に保存されているkeys.datファイルを編集することで鍵を管理できます。ファイルをバックアップしておくことも重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください)
-
+
ゴミ箱を空にしますか?
-
+
ゴミ箱内のメッセージを全て削除してもよろしいですか?
-
+
不正なパスフレーズ
-
+
パスフレーズを入力してください。パスフレーズがない場合は入力する必要はありません。
-
+
不正なアドレスのバージョン番号
-
+
アドレスのバージョン番号は数字にする必要があります: 3 または 4。
-
+
アドレスのバージョン番号は、3 または 4 のどちらかにする必要があります。
@@ -536,22 +507,22 @@ It is important that you back up this file. Would you like to open the file now?
-
+
接続が切断されました
-
+
接続済み
-
+
メッセージが削除されました
-
+
-
+
メッセージが長すぎます
-
+
送信しようとしているメッセージが %1 バイト長すぎます。 (最大は261644バイトです)。 送信する前に短くしてください。
-
+
エラー: アカウントがメールゲートウェイに登録されていません。 今 %1 として登録を送信しています。送信を再試行する前に、登録が処理されるまでお待ちください。
@@ -617,57 +588,57 @@ It is important that you back up this file. Would you like to open the file now?
-
+
エラー: 送信元アドレスを指定してください。まだ作成していない場合には「アドレス一覧」のタブを開いてください。
-
+
アドレスのバージョン番号
-
+
アドレス %1 に接続。%2 のバージョン番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。
-
+
ストリーム番号
-
+
アドレス %1 に接続。%2 のストリーム番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。
-
+
警告: 接続されていません。Bitmessageはメッセージの処理を行いますが、ネットワークに接続するまで送信はされません。
-
+
メッセージがキューに入りました。
-
+
宛先が指定されていません。
-
+
アドレス帳から一つ、または複数のアドレスを右クリックして「このアドレスへ送信」を選んでください。
-
+
namecoin IDからアドレスを取得。
-
+
新規メッセージ
@@ -677,157 +648,157 @@ It is important that you back up this file. Would you like to open the file now?
-
+
-
+ メールゲートウェイの登録リクエストを送信しています
-
+
アドレスが不正です。
-
+
入力されたアドレスは不正です。無視されました。
-
+
エラー: 同じアドレスを複数アドレス帳に追加する事はできません。既存の項目をリネームしてください。
-
+
エラー: 購読に、同じアドレスを2回追加することはできません。 必要に応じて、既存の名前を変更してください。
-
+
再開
-
+
ポート番号の変更を有効にするにはBitmessageを再起動してください。
-
+
プロキシの設定を有効にするには手動でBitmessageを再起動してください。既に接続がある場合は切断されます。
-
+
数字が必要です
-
+
最大ダウンロード数とアップロード数は数字にする必要があります。 入力されたものを無視します。
-
+
今後再送信されません
-
+
入力した時間制限は、Bitmessageが最初の再送試行を待つ時間よりも短いため、メッセージは再送信されないことにご注意ください。
-
+
-
+ メールゲートウェイの登録抹消リクエストを送信しています
-
+
-
+ メールゲートウェイの状態リクエストを送信しています
-
+
パスフレーズが一致しません
-
+
再度入力されたパスフレーズが一致しません。再入力してください。
-
+
パスフレーズを選択してください
-
+
パスフレーズが必要です。
-
+
アドレスが無効になりました
-
+
アドレス %1 が見つかりません。既に削除していませんか?
-
+
アドレスが無効になりました
-
+
エラー: 送信しようとしたアドレスは無効になっています。使用する前に「アドレス一覧」で有効にしてください。
-
+
-
+ アドレス帳に項目が追加されました。ラベルは自由に編集できます。
-
+
ブラックリストに項目が追加されました。ラベルは自由に編集できます。
-
+
エラー: ブラックリストに同じアドレスを2回追加することはできません。 必要に応じて既存の名前を変更してみてください。
-
+
アイテムをゴミ箱へ移動。
-
+
アイテムの削除を元に戻します。
-
+
形式を選択して保存
-
+
書き込みエラー。
-
+
アドレスが未選択です。
-
+
@@ -836,7 +807,7 @@ Are you sure you want to delete the subscription?
購読を削除してもよろしいですか?
-
+
@@ -845,282 +816,282 @@ Are you sure you want to delete the channel?
チャンネルを削除してもよろしいですか?
-
+
このアバターを削除してもよろしいですか?
-
+
すでにこのアドレスのアバターを設定しています。 上書きしてもよろしいですか?
-
+
ログイン時に開始は、まだお使いのOSでサポートされていません。
-
+
トレイに最小化は、まだお使いのOSでサポートされていません。
-
+
トレイ通知は、まだお使いのOSでサポートされていません。
-
+
テスト中
-
+
-
+ chanアドレスは仮想メーリングリストのアドレスには使用できません。
-
+
アドレスは「BM-」から始まります
-
+
このアドレスは正しく入力、またはコピーされていません。(チェックサムが一致しません)。
-
+
このアドレスのバージョン番号はこのプログラムのサポート範囲外です。Bitmessageをアップデートしてください。
-
+
入力されたアドレスは不正な文字を含んでいます。
-
+
このアドレスでエンコードされたデータが短すぎます。
-
+
このアドレスでエンコードされたデータが長過ぎます。
-
+
このアドレスでエンコードされた一部のデータが不正です。
-
+
-
+ 上にアドレスを入力してください。
-
+
アドレスが古い形式です。 過去の配信は表示できません。
-
+
このアドレスから表示する最近の配信はありません。
-
+
-
+ 使用中のポート %1 (設定で変更できます)。
-
+
Bitmessage
-
+
アドレス一覧
-
+
新しいアドレス
-
+
検索
-
+
全て
-
+
宛先
-
+
送信元
-
+
題名
-
+
メッセージ
-
+
受信日時
-
+
メッセージ
-
+
アドレス帳
-
+
アドレス
-
+
連絡先を追加
-
+
namecoin IDを取得
-
+
題名:
-
+
送信元:
-
+
宛先:
-
+
通常のメッセージを送信
-
+
購読者にメッセージを送信
-
+
TTL:
-
+
購読リスト
-
+
購読先を追加
-
+
チャンネル
-
+
チャンネルを追加
-
+
ファイル
-
+
設定
-
+
ヘルプ
-
+
鍵をインポート
-
+
鍵を管理
-
+
Ctrrl+Q
-
+
F1
-
+
お問い合わせサポート
-
+
概要
-
+
deterministicアドレスを再生成
-
+
ゴミ箱のメッセージを全て削除する
-
+
チャンネルに参加 / 作成
-
+
すべてのアカウント
@@ -1130,77 +1101,77 @@ Are you sure you want to delete the channel?
ズーム レベル %1%
-
+
エラー: 同じアドレスを複数リストに追加する事はできません。既存の項目をリネームしてください。
-
+
新しい項目を追加
-
+
-
+ このアドレスから%1の最新の配信を表示します。
-
+
新しいバージョンの PyBitmessage が利用可能です: %1。 https://github.com/Bitmessage/PyBitmessage/releases/latest からダウンロードしてください
-
+
PoW(証明)が完了するのを待っています... %1%
-
+
Pybitmessageをシャットダウンしています... %1%
-
+
オブジェクトの送信待ち... %1%
-
+
設定を保存しています... %1%
-
+
コアをシャットダウンしています... %1%
-
+
通知を停止しています... %1%
-
+
すぐにシャットダウンします... %1%
-
+
%n 時間
-
+
%n 日
-
+
PyBitmessageをシャットダウンしています... %1%
-
+
送信済
@@ -1245,86 +1216,86 @@ Are you sure you want to delete the channel?
アラート: ディスクまたはデータストレージのボリュームがいっぱいです。 Bitmessageが終了します。
-
+
エラー! keys.datファイルで送信元アドレス (あなたのアドレス) を見つけることができませんでした。
-
+
配信に必要な処理を行っています...
-
+
配信が送信されました %1
-
+
暗号鍵は以前にリクエストされました。
-
+
受信者の暗号鍵のリクエストを送信します。
-
+
受信者の公開鍵を探しています
-
+
問題: メッセージに含まれた宛先のリクエストはモバイルデバイスですが、設定では許可されていません。 %1
-
+
メッセージの送信に必要な処理を行っています。
このようなバージョン2のアドレスには、必要な難易度はありません。
-
+
メッセージの送信に必要な処理を行っています。
受信者の必要な難易度: %1 および %2
-
+
問題: 受信者が要求している処理 (%1 および %2) は、現在あなたが設定しているよりも高い難易度です。 %3
-
+
問題: あなた自身またはチャンネルにメッセージを送信しようとしていますが、暗号鍵がkeys.datファイルに見つかりませんでした。 メッセージを暗号化できませんでした。 %1
-
+
メッセージの送信に必要な処理を行っています。
-
+
メッセージを送信しました。 確認応答を待っています。 %1 で送信しました
-
+
暗号鍵のリクエストに必要な処理を行っています。
-
+
公開鍵のリクエストを配信しています。 このプログラムがオフラインの場合、自動的に再試行されます。
-
+
公開鍵のリクエストを送信しています。 返信を待っています。 %1 でリクエストしました
@@ -1339,40 +1310,55 @@ Receiver's required difficulty: %1 and %2
UPnPポートマッピングを削除しました
-
+
すべてのメッセージを既読にする
-
+
すべてのメッセージを既読にしてもよろしいですか?
-
+
配信に必要な処理を行っています。
-
+
PoW(証明)を待っています
-
+
%n オブジェクトが証明待ち (PoW)
-
+
%n オブジェクトが配布待ち
-
+
これらのタスクが完了するまで待ちますか?
+
+
+
+ プロキシとの通信に問題があります: %1。 ネットワーク設定を確認してください。
+
+
+
+
+ SOCKS5認証に問題があります: %1。 SOCKS5の設定を確認してください。
+
+
+
+
+ お使いのコンピュータの時間 %1 は間違っている可能性があります。 設定を確認してください。
+
@@ -1399,37 +1385,32 @@ Receiver's required difficulty: %1 and %2
名前 %1 は関連付けられた Bitmessage アドレスがありません。
-
+
成功! Namecoind バージョン %1 が実行中。
-
+
成功! NMControll が開始して実行中です。
-
+
NMControl を理解できませんでした。
-
-
-
- namecoin への接続に失敗しました。
-
GPUが正しく求められないため、OpenCLが無効になりました。 開発者に報告してください。
-
+
通知音を設定...
-
+
-
+
チャンネルにはお勧めしません
-
+
マナーモード
-
+
接続に問題がありますか? ネットワーク設定でUPnPを有効にしてみてください
-
+
Bitmessage の代わりにメールを送信しようとしています。 これは、ゲートウェイに登録する必要があります。 登録しますか?
-
+
エラー: BitmessageのアドレスはBM-で始まります。 受信者のアドレス %1 を確認してください
-
+
エラー: 受信者のアドレス %1 は正しく入力、またはコピーされていません。確認して下さい。
-
+
エラー: 受信者のアドレス %1 は不正な文字を含んでいます。確認して下さい。
-
+
エラー: 受信者アドレスのバージョン %1 は高すぎます。 Bitmessageソフトウェアをアップグレードする必要があるか、連絡先が賢明になっているかのいずれかです。
-
+
エラー: アドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。
-
+
エラー: 受信者のアドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。
-
+
エラー: 受信者のアドレス %1 でエンコードされたデータの一部が不正です。連絡先のソフトウェアが何かしら誤っている可能性があります。
-
+
エラー: 受信者のアドレス %1 には何かしら誤りがあります。
-
+
- エラー: %1
+
-
+
送信元 %1
-
+
同期を保留しています
-
+
Bitmessageはネットワークと同期していません。%n のオブジェクトをダウンロードする必要があります。 今、終了すると、配送が遅れることがあります。 同期が完了するまで待ちますか?
-
+
未接続
-
+
Bitmessageはネットワークに接続していません。 今、終了すると、配送が遅れることがあります。 接続して、同期が完了するまで待ちますか?
-
+
ネットワーク接続を待っています...
-
+
同期の完了を待っています...
-
+
すでにこのアドレス帳エントリの通知音を設定しています。 上書きしてもよろしいですか?
-
-
-
- エラーが発生しました: ディスクからメッセージを読み込みできません。
-
-
-
-
- このアドレスから%nの最新の配信を表示します。
-
-
-
-
- オンラインにする
-
-
-
-
- オフラインにする
-
-
-
-
- クリア
-
-
-
-
- 受信トレイ
-
-
-
-
- 新規
-
-
-
-
- 送信済
-
-
-
-
- ゴミ箱
-
MessageView
-
+
外部リンクをフォロー
-
+
リンク "%1" はブラウザで開きます。 セキュリティリスクの可能性があります。匿名性がなくなったり、悪意のあるデータをダウンロードする可能性があります。 よろしいですか?
-
+
HTMLが検出されました。ここをクリックすると表示します
-
+
ここをクリックするとHTMLを無効にします
@@ -1620,14 +1556,14 @@ Receiver's required difficulty: %1 and %2
MsgDecode
-
+
メッセージのエンコードが不明です。
Bitmessageをアップグレードする必要があるかもしれません。
-
+
不明なエンコード
@@ -1635,98 +1571,98 @@ Bitmessageをアップグレードする必要があるかもしれません。<
NewAddressDialog
-
+
新しいアドレスを作成
-
+
複数のアドレスを生成できます。アドレスを自由に生成、破棄することができます。アドレスは乱数かパスフレーズを使って生成できます。もしパスフレーズを使う場合、アドレスはdeterministicアドレスになります。デフォルトでは乱数による生成が選択されますが、deterministicアドレスにも長所と短所があります:
-
+
<html><head/><body><p><span style=" font-weight:600;">長所:<br/></span>記憶を頼りにアドレスを再生成できます。<br/>keys.datファイルのバックアップの心配をしないでも、パスフレーズを覚えておけばよくなります。<br/><span style=" font-weight:600;">短所:<br/></span>アドレスの暗号鍵を紛失した場合に備えてアドレスを再生成出来るようにしたい場合、パスフレーズを覚えて(もしくは書き留めて)必要があります。<br/>パスフレーズを覚えておくのに加えて、アドレスのバージョン番号とストリーム番号も覚えておく必要があります。<br/>弱いパスフレーズを設定すると、ネット上の誰かがブルートフォース攻撃を行ってあなたの送信メッセージ、受信メッセージを読んでしまう可能性があります。</p></body></html>
-
+
アドレスの生成に乱数ジェネレーターを使う
-
+
アドレスの作成にパスフレーズを使う
-
+
アドレスを1、2文字短くするために数分間追加の計算処理を行う
-
+
deterministicアドレスを作る
-
+
アドレスのバージョン番号: 4
-
+
パスフレーズに加えて、これらの値を覚えておいてください:
-
+
パスフレーズ
-
+
パスフレーズから生成されたアドレスの数:
-
+
ストリーム数: 1
-
+
パスフレーズを再入力
-
+
ランダムなアドレスを生成する
-
+
ラベル(他の人からは見えません)
-
+
最も有効なストリームを使う
-
+
(もしこれから複数のアドレスを生成するのであれば、最初の一つに最適です。)
-
+
既存のアドレスと同じストリームを利用する
-
+
(帯域と処理能力を節約する)
@@ -1734,22 +1670,22 @@ The 'Random Number' option is selected by default but deterministic ad
NewSubscriptionDialog
-
+
新しい項目を追加
-
+
ラベル
-
+
アドレス
-
+
上にアドレスを入力してください。
@@ -1757,60 +1693,55 @@ The 'Random Number' option is selected by default but deterministic ad
SpecialAddressBehaviorDialog
-
+
アドレスの特別な動作
-
+
通常のアドレスにする
-
+
仮想メーリングリストとして使用する
-
+
仮想メーリングリストのアドレスが受信したアドレスは自動的に購読するユーザーに配信(公開)されます。
-
+
仮想メーリングリストの名前:
-
-
-
- chanアドレスは仮想メーリングリストのアドレスには使用できません。
-
aboutDialog
-
+
概要
-
-
-
-
-
-
-
+
+ PyBitmessage
-
+
+
+ バージョン?
+
+
+
<html><head/><body><p>MIT/X11 ソフトウェアライセンスに基づいて配布されます。 <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a> をご覧ください</p></body></html>
-
+
このソフトウェアはベータ版です。
@@ -1819,10 +1750,10 @@ The 'Random Number' option is selected by default but deterministic ad
-
-
-
- <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html>
+
+
+
+ <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2016 The Bitmessage 開発者</p></body></html>
@@ -1853,12 +1784,12 @@ The 'Random Number' option is selected by default but deterministic ad
アドレス
-
+
ブラックリスト
-
+
ホワイトリスト
@@ -1866,45 +1797,40 @@ The 'Random Number' option is selected by default but deterministic ad
connectDialog
-
+
Bitmessage
-
+
Bitmessageはあなたが操作しない限りどこへも接続しません。
-
+
接続
-
+
最初に特別なネットワークの設定を行ってください
-
-
-
-
-
helpDialog
-
+
ヘルプ
-
+
<a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a>
-
+
Bitmessageは協働プロジェクトです。ヘルプはBitmessage Wikiを参照してください:
@@ -1912,35 +1838,30 @@ The 'Random Number' option is selected by default but deterministic ad
iconGlossaryDialog
-
+
アイコン一覧
-
+
他のpeerへ接続されていません。
-
+
発信接続のために1つ以上のピアへ接続を行っていますが、まだ着信接続を受け取っていません。ファイアーウォールかホームルーターが外部からこのコンピューターへのTCP接続を受け取れるように設定されていないかも知れません。Bitmessageは正常に動作しますが、外部からの接続を許可してより良く接続されたノードになることはBitmessageネットワークへの助けになります。
-
+ 使用中のポート ? (設定で変更できます)。
-
+
ファイアーウォールを適切に設定し、他のpeerへ接続してください。
-
-
-
- 使用中のポート %1 (設定で変更できます)。
-
networkstatus
@@ -1950,37 +1871,37 @@ The 'Random Number' option is selected by default but deterministic ad
接続数:
-
+
起動日時:
-
+
0 通の1対1のメッセージを処理しました。
-
+
0 件の公開鍵を処理しました。
-
+
0 件の配信を処理しました。
-
+
毎秒のインベントリ検索: 0
-
+
同期する必要のあるオブジェクト:
-
+
ストリーム #
@@ -1990,112 +1911,112 @@ The 'Random Number' option is selected by default but deterministic ad
-
+
起動日時 %1
-
+
ダウン: %1/秒 合計: %2
-
+
アップ: %1/秒 合計: %2
-
+
接続数: %1
-
+
毎秒のインベントリ検索: %1
-
+
アップ: 0 kB/秒
-
+
ダウン: 0 kB/秒
-
+
ネットワークの状態
-
+
バイト
-
+
同期する必要のあるオブジェクト: %n
-
+
%n 通の1対1のメッセージを処理しました。
-
+
%n 件の配信を処理しました。
-
+
%n 件の公開鍵を処理しました。
-
+
ピア
-
+
IP アドレスまたはホスト名
-
+
評価
-
+
PyBitmessage は、個々のノードへの接続試行の成功率を追跡します。 率は -1 から 1 の範囲で、将来ノードを選択する可能性に影響します
-
+
ユーザーエージェント
-
+
ピアの自己報告ソフトウェア
-
+
TLS
-
+
接続暗号化
-
+
あなたとピアの間でネゴシエーションしたストリームのリスト
@@ -2155,7 +2076,7 @@ The 'Random Number' option is selected by default but deterministic ad
- チャンネルのパスフレーズ/名前:
+
@@ -2181,12 +2102,12 @@ The 'Random Number' option is selected by default but deterministic ad
チャンネル %1 を正常に作成 / 参加しました
-
+
チャンネルの作成 / 参加に失敗しました
-
+
チャンネルの作成 / 参加をキャンセルしました
@@ -2220,52 +2141,52 @@ The 'Random Number' option is selected by default but deterministic ad
regenerateAddressesDialog
-
+
既存のアドレスを再生成する
-
+
-
+ 既存のアドレスを再生成する
-
+
パスフレーズ
-
+
パスフレーズから生成されたアドレスの数:
-
+
アドレスのバージョン番号:
-
+
ストリーム数:
-
+
1
-
+
アドレスを1、2文字短くするために数分間追加の計算処理を行う
-
+
もしあなたが初めてアドレスを作ったのであればこのボックスをチェックする必要があります。(そうでない場合はしないでください)。
-
+
もし以前にdeterministicアドレスを作ったことがあり、何かしらのトラブル(ハードディスクの故障のような)でそれを紛失していた場合、ここで再生成することができます。もし乱数でアドレスを作っていたのであればこのフォームは再生成には使えません。
diff --git a/src/translations/bitmessage_pl.qm b/src/translations/bitmessage_pl.qm
index 94e7b8ca..3b9a0dcc 100644
Binary files a/src/translations/bitmessage_pl.qm and b/src/translations/bitmessage_pl.qm differ
diff --git a/src/translations/bitmessage_pl.ts b/src/translations/bitmessage_pl.ts
index c10259d7..c97cb4bb 100644
--- a/src/translations/bitmessage_pl.ts
+++ b/src/translations/bitmessage_pl.ts
@@ -60,27 +60,27 @@
@mailchuck.com
-
+
Rejestracja nie powiodła się:
-
+
Wybrany adres e-mail nie jest dostępny, proszę spróbować inny.
-
+
Wysyłanie zapytania o rejestrację na bramce poczty
-
+
Wysyłanie zapytania o wyrejestrowanie z bramki poczty
-
+
Wysyłanie zapytania o stan bramki poczty
@@ -199,52 +199,52 @@ Please type the desired email address (including @mailchuck.com) below:
MainWindow
-
+
Odpowiedz do nadawcy
-
+
Odpowiedz do kanału
-
+
Dodaj nadawcę do Książki Adresowej
-
+
Dodaj nadawcę do Listy Blokowanych
-
+
Przenieś do kosza
-
+
Przywróć
-
+
Wyświetl kod HTML w postaci sformatowanej
-
+
Zapisz wiadomość jako…
-
+
Oznacz jako nieprzeczytane
-
+
Nowe
@@ -269,12 +269,12 @@ Please type the desired email address (including @mailchuck.com) below:
Kopiuj adres do schowka
-
+
Specjalne zachowanie adresu…
-
+
Przekaźnik e-mail
@@ -284,37 +284,37 @@ Please type the desired email address (including @mailchuck.com) below:
Usuń
-
+
Wyślij wiadomość pod ten adres
-
+
Subskrybuj ten adres
-
+
Dodaj nowy adres
-
+
Kopiuj adres odbiorcy do schowka
-
+
Wymuś wysłanie
-
+
Jeden z adresów, %1, jest starym adresem wersji 1. Adresy tej wersji nie są już wspierane. Usunąć go?
-
+
Oczekiwanie na klucz szyfrujący odbiorcy. Niedługo nastąpi ponowne wysłanie o niego prośby.
@@ -324,17 +324,17 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
W kolejce do wysłania.
-
+
Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1
-
+
Wiadomość wysłana. Wysłano o %1
@@ -344,77 +344,77 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
Otrzymano potwierdzenie odbioru wiadomości %1
-
+
Przekaz w kolejce do wysłania.
-
+
Wysłana o %1
-
+
Problem: dowód pracy wymagany przez odbiorcę jest trudniejszy niż zaakceptowany przez Ciebie. %1
-
+
Problem: klucz szyfrujący odbiorcy jest nieprawidłowy. Nie można zaszyfrować wiadomości. %1
-
+
Wymuszono ominięcie trudności. Wysłanie zostanie wkrótce rozpoczęte.
-
+
Nieznany status: %1 %2
-
+
Brak połączenia
-
+
Pokaż Bitmessage
-
+
Wyślij
-
+
Subskrybuj
-
+
Kanał
-
+
Zamknij
-
+
Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się w tym samym katalogu co program. Zaleca się zrobienie kopii zapasowej tego pliku.
-
+
@@ -423,17 +423,17 @@ It is important that you back up this file.
Zaleca się zrobienie kopii zapasowej tego pliku.
-
+
Otworzyć plik keys.dat?
-
+
Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się w tym samym katalogu co program. Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik teraz? (Zamknij Bitmessage, przed wprowadzeniem jakichkolwiek zmian.)
-
+
@@ -442,37 +442,37 @@ It is important that you back up this file. Would you like to open the file now?
Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik teraz? (Zamknij Bitmessage przed wprowadzeniem jakichkolwiek zmian.)
-
+
Opróżnić kosz?
-
+
Czy na pewno usunąć wszystkie wiadomości z kosza?
-
+
nieprawidłowe hasło
-
+
Musisz wpisać swoje hasło. Jeżeli go nie posiadasz, to ten formularz nie jest dla Ciebie.
-
+
Nieprawidłowy numer wersji adresu
-
+
Twój numer wersji adresu powinien wynosić: 3 lub 4.
-
+
Twój numer wersji adresu powinien wynosić: 3 lub 4.
@@ -542,22 +542,22 @@ Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik
-
+
Połączenie utracone
-
+
Połączono
-
+
Wiadomość usunięta
-
+
-
+
Wiadomość zbyt długa
-
+
Wiadomość jest za długa o %1 bajtów (maksymalna długość wynosi 261644 bajty). Przed wysłaniem należy ją skrócić.
-
+
Błąd: Twoje konto nie było zarejestrowane w bramce poczty. Rejestrowanie jako %1, proszę poczekać na zakończenie procesu przed ponowną próbą wysłania wiadomości.
@@ -623,57 +623,57 @@ Zwykle 4-5 dniowy TTL jest odpowiedni.
-
+
Błąd: musisz wybrać adres wysyłania. Jeżeli go nie posiadasz, przejdź do zakładki 'Twoje tożsamości'.
-
+
Numer wersji adresu
-
+
Odnośnie adresu %1, Bitmessage nie potrafi odczytać wersji adresu %2. Może uaktualnij Bitmessage do najnowszej wersji.
-
+
Numer strumienia
-
+
Odnośnie adresu %1, Bitmessage nie potrafi operować na strumieniu adresu %2. Może uaktualnij Bitmessage do najnowszej wersji.
-
+
Uwaga: nie jesteś obecnie połączony. Bitmessage wykona niezbędną pracę do wysłania wiadomości, ale nie wyśle jej póki się nie połączysz.
-
+
W kolejce do wysłania
-
+
Pole 'Do' jest puste
-
+
Użyj prawego przycisku myszy na adresie z książki adresowej i wybierz opcję "Wyślij wiadomość do tego adresu".
-
+
Pobrano adres z identyfikatora Namecoin.
-
+
Nowa wiadomość
@@ -698,47 +698,47 @@ Zwykle 4-5 dniowy TTL jest odpowiedni.
Wprowadzono niewłaściwy adres, który został zignorowany.
-
+
Błąd: Adres znajduje się już w książce adresowej.
-
+
Błąd: Adres znajduje się już na liście subskrybcji.
-
+
Uruchom ponownie
-
+
Musisz zrestartować Bitmessage, aby zmiana numeru portu weszła w życie.
-
+
Bitmessage będzie of teraz korzystał z serwera proxy, ale możesz ręcznie zrestartować Bitmessage, aby zamknąć obecne połączenia (jeżeli występują).
-
+
Wymagany numer
-
+
Maksymalne prędkości wysyłania i pobierania powinny być liczbami. Zignorowano zmiany.
-
+
Nigdy nie wysyłaj ponownie
-
+
Zauważ, że wpisany limit czasu wynosi mniej niż czas, który Bitmessage czeka przed pierwszą ponowną próbą wysłania wiadomości, więc Twoje wiadomości nie zostaną nigdy wysłane ponownie.
@@ -773,22 +773,22 @@ Zwykle 4-5 dniowy TTL jest odpowiedni.
Naprawdę musisz wpisać hasło.
-
+
Adres zniknął
-
+
Bitmessage nie może odnaleźć Twojego adresu %1. Może go usunąłeś?
-
+
Adres nieaktywny
-
+
Błąd: adres, z którego próbowałeś wysłać wiadomość jest nieaktywny. Włącz go w zakładce 'Twoje tożsamości' zanim go użyjesz.
@@ -798,42 +798,42 @@ Zwykle 4-5 dniowy TTL jest odpowiedni.
-
+
Dodano wpis do listy blokowanych. Można teraz zmienić jego nazwę.
-
+
Błąd: Adres znajduje się już na liście blokowanych.
-
+
Przeniesiono wiadomości do kosza.
-
+
Przywrócono wiadomość.
-
+
Zapisz jako…
-
+
Błąd zapisu.
-
+
Nie wybrano adresu.
-
+
@@ -842,7 +842,7 @@ Are you sure you want to delete the subscription?
Czy na pewno chcesz usunąć tę subskrypcję?
-
+
@@ -851,32 +851,32 @@ Are you sure you want to delete the channel?
Czy na pewno chcesz usunąć ten kanał?
-
+
Czy na pewno chcesz usunąć ten awatar?
-
+
Już ustawiłeś awatar dla tego adresu. Czy na pewno chcesz go nadpisać?
-
+
Start po zalogowaniu jeszcze nie jest wspierany pod Twoim systemem.
-
+
Minimalizacja do zasobnika nie jest jeszcze wspierana pod Twoim systemem.
-
+
Powiadomienia w zasobniku nie są jeszcze wspierane pod Twoim systemem.
-
+
Testowanie…
@@ -941,192 +941,192 @@ Czy na pewno chcesz usunąć ten kanał?
-
+
Bitmessage
-
+
Tożsamości
-
+
Nowa tożsamość
-
+
Szukaj
-
+
Wszystkie
-
+
Do
-
+
Od
-
+
Temat
-
+
Wiadomość
-
+
Odebrana
-
+
Wiadomości
-
+
Książka adresowa
-
+
Adres
-
+
Dodaj kontakt
-
+
Pobierz Namecoin ID
-
+
Temat:
-
+
Od:
-
+
Do:
-
+
Wyślij zwykłą wiadomość
-
+
Wyślij wiadomość broadcast
-
+
Czas życia:
-
+
Subskrypcje
-
+
Dodaj subskrypcję
-
+
Kanały
-
+
Dodaj kanał
-
+
Plik
-
+
Ustawienia
-
+
Pomoc
-
+
Importuj klucze
-
+
Zarządzaj kluczami
-
+
Ctrl+Q
-
+
F1
-
+
Kontakt z twórcami
-
+
O programie
-
+
Odtwórz adres deterministyczny
-
+
Usuń wiadomości z kosza
-
+
Dołącz / Utwórz kanał
-
+
Wszystkie konta
@@ -1146,67 +1146,67 @@ Czy na pewno chcesz usunąć ten kanał?
Dodaj nowy wpis
-
+
-
+ Wyświetl %1 ostatnich wiadomości subskrypcji z tego adresu.
-
+
Nowa wersja Bitmessage jest dostępna: %1. Pobierz ją z https://github.com/Bitmessage/PyBitmessage/releases/latest
-
+
Oczekiwanie na wykonanie dowodu pracy… %1%
-
+
Zamykanie PyBitmessage… %1%
-
+
Oczekiwanie na wysłanie obiektów… %1%
-
+
Zapisywanie ustawień… %1%
-
+
Zamykanie rdzenia programu… %1%
-
+
Zatrzymywanie powiadomień… %1%
-
+
Zaraz zamknę… %1%
-
+
%n godzina%n godziny%n godzin%n godzin
-
+
%n dzień%n dni%n dni%n dni
-
+
Zamykanie PyBitmessage… %1%
-
+
Wysłane
@@ -1310,7 +1310,7 @@ Odbiorca wymaga trudności: %1 i %2
Problem: próbujesz wysłać wiadomość do siebie lub na kanał, ale Twój klucz szyfrujący nie został znaleziony w pliku keys.dat. Nie można zaszyfrować wiadomości. %1
-
+
Wykonywanie pracy potrzebnej do wysłania wiadomości.
@@ -1320,7 +1320,7 @@ Odbiorca wymaga trudności: %1 i %2
Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1
-
+
Wykonywanie pracy niezbędnej do prośby o klucz szyfrujący.
@@ -1345,37 +1345,37 @@ Odbiorca wymaga trudności: %1 i %2
Usunięto mapowanie portów UPnP
-
+
Oznacz wszystkie jako przeczytane
-
+
Czy na pewno chcesz oznaczyć wszystkie wiadomości jako przeczytane?
-
+
Wykonywanie dowodu pracy niezbędnego do wysłania przekazu.
-
+
Dowód pracy zawieszony
-
+
Zawieszony dowód pracy %n obiektuZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektówZawieszony dowód pracy %n obiektów
-
+
%n obiekt oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie%n obiektów oczekuje na wysłanie
-
+
Czy poczekać aż te zadania zostaną zakończone?
@@ -1420,37 +1420,32 @@ Odbiorca wymaga trudności: %1 i %2
Ksywka %1 nie ma powiązanego adresu Bitmessage.
-
+
Namecoind wersja %1 działa poprawnie!
-
+
NMControl działa poprawnie!
-
+
Nie można zrozumieć NMControl.
-
-
-
- Nie udało się połączyć z namecoin.
-
Twoje procesory graficzne nie obliczyły poprawnie, wyłączam OpenCL. Prosimy zaraportować przypadek twórcom programu.
-
+
Ustaw dźwięk powiadomień…
-
+
-
+
niezalecany dla kanałów
-
+
Tryb cichy
-
+
Problem z połączeniem? Spróbuj włączyć UPnP w ustawieniach sieci.
-
+
Próbujesz wysłać e-mail zamiast wiadomość bitmessage. To wymaga zarejestrowania się na bramce. Czy zarejestrować?
-
+
Błąd: adresy Bitmessage zaczynają się od BM-. Proszę sprawdzić adres odbiorcy %1.
-
+
Błąd: adres odbiorcy %1 nie został skopiowany lub przepisany poprawnie. Proszę go sprawdzić.
-
+
Błąd: adres odbiorcy %1 zawiera nieprawidłowe znaki. Proszę go sprawdzić.
-
+
Błąd: wersja adresu odbiorcy %1 jest za wysoka. Musisz albo zaktualizować Twoje oprogramowanie Bitmessage, albo twój znajomy Cię trolluje.
-
+
Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt krótkie. Być może coś nie działa należycie w programie Twojego znajomego.
-
+
Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt długie. Być może coś nie działa należycie w programie Twojego znajomego.
-
+
Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są uszkodzone. Być może coś nie działa należycie w programie Twojego znajomego.
-
+
Błąd: coś jest nie tak z adresem odbiorcy %1.
-
+
Błąd: %1
-
+
Od %1
-
+
Synchronizacja zawieszona
-
+
Bitmessage nie zsynchronizował się z siecią, %n obiekt oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiekty oczekują na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?Bitmessage nie zsynchronizował się z siecią, %n obiektów oczekuje na pobranie. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na zakończenie synchronizacji?
-
+
Niepołączony
-
+
Bitmessage nie połączył się z siecią. Jeżeli zamkniesz go teraz, może to spowodować opóźnienia dostarczeń. Czy poczekać na połączenie i zakończenie synchronizacji?
-
+
Oczekiwanie na połączenie sieciowe…
-
+
Oczekiwanie na zakończenie synchronizacji…
-
+
Już ustawiłeś dźwięk powiadomienia dla tego kontaktu. Czy chcesz go zastąpić?
-
-
- Wystąpił błąd: nie można załadować wiadomości z dysku.
-
-
-
-
- Wyświetl %n ostatnią wiadomość przekazu z tego adresu.Wyświetl %n ostatnią wiadomość przekazu z tego adresu.Wyświetl %n ostatnich wiadomości przekazu z tego adresu.Wyświetl %n ostatnich wiadomości przekazu z tego adresu.
-
-
-
+
Połącz
-
+
Rozłącz
-
-
-
- Wymaż
-
-
-
-
- odebrane
-
-
-
-
- nowe
-
-
-
-
- wysłane
-
-
-
-
- kosz
-
MessageView
@@ -1640,14 +1600,14 @@ Witamy w przyjaznym i bezpiecznym Bitmessage
MsgDecode
-
+
Wiadomość zawiera nierozpoznane kodowanie.
Prawdopodobnie powinieneś zaktualizować Bitmessage.
-
+
Nierozpoznane kodowanie
@@ -1803,7 +1763,7 @@ Generowanie adresów „losowych” jest wybrane domyślnie, jednak deterministy
Nazwa pseudo-listy-dyskusyjnej:
-
+
To jest adres kanału. Nie możesz go użyć jako pseudo-listy-dyskusyjnej.
@@ -1826,12 +1786,12 @@ Generowanie adresów „losowych” jest wybrane domyślnie, jednak deterministy
-
+
<html><head/><body><p>Rozpowszechniane na licencji MIT/X11 software license; zobacz <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html>
-
+
To jest wersja Beta.
@@ -1841,7 +1801,7 @@ Generowanie adresów „losowych” jest wybrane domyślnie, jednak deterministy
-
+
<html><head/><body><p>Prawa autorskie © 2012-2016 Jonathan Warren<br/>Prawa autorskie © 2013-2017 Programiści Bitmessage</p></body></html>
@@ -2011,27 +1971,27 @@ Generowanie adresów „losowych” jest wybrane domyślnie, jednak deterministy
-
+
Od startu programu o %1
-
+
Pobieranie: %1/s W całości: %2
-
+
Wysyłanie: %1/s W całości: %2
-
+
Wszystkich połączeń: %1
-
+
Zapytań o elementy na sekundę: %1
@@ -2046,32 +2006,32 @@ Generowanie adresów „losowych” jest wybrane domyślnie, jednak deterministy
Pobieranie: 0 kB/s
-
+
Stan sieci
-
+
bajtbajtówbajtówbajtów
-
+
Jeden obiekt do zsynchronizowaniaObieków do zsynchronizowania: %nObieków do zsynchronizowania: %nObieków do zsynchronizowania: %n
-
+
Przetworzono %n wiadomość zwykłą.Przetworzono %n wiadomości zwykłych.Przetworzono %n wiadomości zwykłych.Przetworzono %n wiadomości zwykłych.
-
+
Przetworzono %n wiadomość subskrypcji.Przetworzono %n wiadomości subskrypcji.Przetworzono %n wiadomości subskrypcji.Przetworzono %n wiadomości subskrypcji.
-
+
Przetworzono %n klucz publiczny.Przetworzono %n kluczy publicznych.Przetworzono %n kluczy publicznych.Przetworzono %n kluczy publicznych.
diff --git a/src/translations/bitmessage_zh_cn.qm b/src/translations/bitmessage_zh_cn.qm
index 204047e1..cc37b146 100644
Binary files a/src/translations/bitmessage_zh_cn.qm and b/src/translations/bitmessage_zh_cn.qm differ
diff --git a/src/translations/bitmessage_zh_cn.ts b/src/translations/bitmessage_zh_cn.ts
index 3a5c4aab..58e16cb8 100644
--- a/src/translations/bitmessage_zh_cn.ts
+++ b/src/translations/bitmessage_zh_cn.ts
@@ -2,17 +2,17 @@
AddAddressDialog
-
+
添加新条目
-
+
标签
-
+
地址
@@ -20,99 +20,69 @@
EmailGatewayDialog
-
+
电子邮件网关
-
+
注册电子邮件网关
-
+
电子邮件网关帐户状态
-
+
更改电子邮件网关帐户设置
-
+
取消电子邮件网关注册
-
+
电子邮件网关允许您与电子邮件用户通信。目前,只有Mailchuck电子邮件网关(@mailchuck.com)可用。
-
+
所需的电子邮件地址(包括 @mailchuck.com):
-
-
-
- @mailchuck.com
-
-
-
-
- 注册失败:
-
-
-
-
- 请求的电子邮件地址不可用,请换一个新的试试。
-
-
-
-
- 发送电子邮件网关注册请求
-
-
-
-
- 发送电子邮件网关注销请求
-
-
-
-
- 发送电子邮件网关状态请求
-
EmailGatewayRegistrationDialog
-
+
-
+ 注册失败:
-
+
-
+ 要求的电子邮件地址不详,请尝试一个新的。填写新的所需电子邮件地址(包括 @mailchuck.com)如下:
-
+ 电子邮件网关注册
-
+ 电子邮件网关允许您与电子邮件用户通信。目前,只有Mailchuck电子邮件网关(@mailchuck.com)可用。请键入所需的电子邮件地址(包括 @mailchuck.com)如下:
Mailchuck
-
+
MainWindow
-
+
回复发件人
-
+
回复通道
-
+
将发送者添加到您的通讯簿
-
+
将发件人添加到您的黑名单
-
+
移入回收站
-
+
取消删除
-
+
作为HTML查看
-
+
将消息保存为...
-
+
标记为未读
-
+
新建
@@ -266,12 +236,12 @@ Please type the desired email address (including @mailchuck.com) below:
将地址复制到剪贴板
-
+
特别的地址行为...
-
+
电子邮件网关
@@ -281,37 +251,37 @@ Please type the desired email address (including @mailchuck.com) below:
删除
-
+
发送消息到这个地址
-
+
订阅到这个地址
-
+
创建新地址
-
+
复制目标地址到剪贴板
-
+
强制发送
-
+
您的地址中的一个, %1,是一个过时的版本1地址. 版本1地址已经不再受到支持了. 我们可以将它删除掉么?
-
+
正在等待他们的加密密钥,我们会在稍后再次请求。
@@ -321,17 +291,17 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
已经添加到队列。
-
+
消息已经发送. 正在等待回执. 发送于 %1
-
+
消息已经发送. 发送于 %1
@@ -341,131 +311,131 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
消息的回执已经收到于 %1
-
+
广播已经添加到队列中。
-
+
已经广播于 %1
-
+
错误: 收件人要求的做工量大于我们的最大接受做工量。 %1
-
+
错误: 收件人的加密密钥是无效的。不能加密消息。 %1
-
+
已经忽略最大做工量限制。发送很快就会开始。
-
+
未知状态: %1 %2
-
+
未连接
-
+
显示比特信
-
+
发送
-
+
订阅
-
+
频道
-
+
退出
-
+
您可以通过编辑和程序储存在同一个目录的 keys.dat 来编辑密钥。备份这个文件十分重要。
-
+
您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。
-
+
打开 keys.dat ?
-
+
您可以通过编辑和程序储存在同一个目录的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信)
-
+
您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信)
-
+
清空回收站?
-
+
您确定要删除全部被回收的消息么?
-
+
错误的密钥
-
+
您必须输入您的密钥。如果您没有的话,这个表单不适用于您。
-
+
地址的版本号无效
-
+
您的地址的版本号必须是一个数字: 3 或 4.
-
+
您的地址的版本号必须是 3 或 4.
@@ -535,22 +505,22 @@ It is important that you back up this file. Would you like to open the file now?
-
+
连接已丢失
-
+
已经连接
-
+
消息已经移入回收站
-
+
-
+
信息太长
-
+
你正在尝试发送的信息已超过%1个字节太长, (最大为261644个字节). 发送前请剪下来。
-
+
错误: 您的帐户没有在电子邮件网关注册。现在发送注册为%1, 注册正在处理请稍候重试发送.
@@ -614,57 +584,57 @@ It is important that you back up this file. Would you like to open the file now?
-
+
错误: 您必须指出一个表单地址, 如果您没有,请到“您的身份”标签页。
-
+
地址版本号
-
+
地址 %1 的地址版本号 %2 无法被比特信理解。也许你应该升级你的比特信到最新版本。
-
+
节点流序号
-
+
地址 %1 的节点流序号 %2 无法被比特信理解。也许你应该升级你的比特信到最新版本。
-
+
警告: 您尚未连接。 比特信将做足够的功来发送消息,但是消息不会被发出直到您连接。
-
+
信息排队。
-
+
“收件人"是空的。
-
+
在您的地址本的一个条目上右击,之后选择”发送消息到这个地址“。
-
+
已经自namecoin接收了地址。
-
+
新消息
@@ -674,9 +644,9 @@ It is important that you back up this file. Would you like to open the file now?
-
+
-
+ 发送电子邮件网关注册请求
@@ -689,142 +659,142 @@ It is important that you back up this file. Would you like to open the file now?
您输入的地址是无效的,将被忽略。
-
+
错误:您无法将一个地址添加到您的地址本两次,请尝试重命名已经存在的那个。
-
+
错误: 您不能在同一地址添加到您的订阅两次. 也许您可重命名现有之一.
-
+
重启
-
+
您必须重启以便使比特信对于使用的端口的改变生效。
-
+
比特信将会从现在开始使用代理,但是您可能想手动重启比特信以便使之前的连接关闭(如果有的话)。
-
+
需求数字
-
+
您最大的下载和上传速率必须是数字. 忽略您键入的内容.
-
+
不尝试再次发送
-
+
请注意,您所输入的时间限制小于比特信的最小重试时间,因此您将永远不会重发消息。
-
+
-
+ 发送电子邮件网关注销请求
-
+
-
+ 发送电子邮件网关状态请求
-
+
密钥不匹配
-
+
您两次输入的密码并不匹配,请再试一次。
-
+
选择一个密钥
-
+
您真的需要一个密码。
-
+
已经失去了地址
-
+
比特信无法找到你的地址 %1。 也许你已经把它删掉了?
-
+
地址已经禁用
-
+
错误: 您想以一个您已经禁用的地址发出消息。在使用之前您需要在“您的身份”处再次启用。
-
+
-
+ 条目已经添加到地址本。您可以去修改您的标签。
-
+
条目添加到黑名单. 根据自己的喜好编辑标签.
-
+
错误: 您不能在同一地址添加到您的黑名单两次. 也许您可重命名现有之一.
-
+
已经移动项目到回收站。
-
+
未删除的项目。
-
+
另存为...
-
+
写入失败。
-
+
没有选择地址。
-
+
@@ -833,7 +803,7 @@ Are you sure you want to delete the subscription?
你确定要删除订阅?
-
+
@@ -842,282 +812,282 @@ Are you sure you want to delete the channel?
你确定要删除频道?
-
+
您真的想移除这个头像么?
-
+
您已经为这个地址设置了头像了。您真的想移除么?
-
+
登录时启动尚未支持您在使用的操作系统。
-
+
最小化到托盘尚未支持您的操作系统。
-
+
托盘提醒尚未支持您所使用的操作系统。
-
+
正在测试...
-
+
-
+ 这是一个频道地址,您无法把它作为伪邮件列表。
-
+
地址应该以"BM-"开始
-
+
地址没有被正确的键入或复制(校验码校验失败)。
-
+
这个地址的版本号大于此软件的最大支持。 请升级比特信。
-
+
这个地址中包含无效字符。
-
+
在这个地址中编码的部分信息过少。
-
+
在这个地址中编码的部分信息过长。
-
+
在地址编码的某些数据格式不正确.
-
+
-
+ 请在上方键入地址。
-
+
地址没有近期的广播。我们无法显示之间的广播。
-
+
没有可以显示的近期广播。
-
+
-
+ 您正在使用TCP端口 %1 。(可以在设置中修改)。
-
+
比特信
-
+
身份标识
-
+
新身份标识
-
+
搜索
-
+
全部
-
+
至
-
+
来自
-
+
标题
-
+
消息
-
+
接收时间
-
+
信息
-
+
地址簿
-
+
地址
-
+
增加联系人
-
+
接收Namecoin ID
-
+
标题:
-
+
来自:
-
+
至:
-
+
发送普通信息
-
+
发送信息给您的订户
-
+
TTL:
-
+
订阅
-
+
添加新的订阅
-
+
Chans
-
+
添加 Chans
-
+
文件
-
+
设置
-
+
帮助
-
+
导入密钥
-
+
管理密钥
-
+
Ctrl+Q
-
+
F1
-
+
联系支持
-
+
关于
-
+
重新生成静态地址
-
+
彻底删除全部回收站中的消息
-
+
加入或创建一个频道
-
+
所有帐户
@@ -1137,67 +1107,67 @@ Are you sure you want to delete the channel?
添加新条目
-
+
-
+ 显示从这个地址%1的最近广播
-
+
PyBitmessage的新版本可用: %1. 从https://github.com/Bitmessage/PyBitmessage/releases/latest下载
-
+
等待PoW完成...%1%
-
+
关闭Pybitmessage ...%1%
-
+
等待要发送对象...%1%
-
+
保存设置...%1%
-
+
关闭核心...%1%
-
+
停止通知...%1%
-
+
关闭即将来临...%1%
-
+
%n 小时
-
+
%n 天
-
+
关闭PyBitmessage...%1%
-
+
发送
@@ -1242,86 +1212,86 @@ Are you sure you want to delete the channel?
警告: 您的磁盘或数据存储量已满. 比特信将立即退出.
-
+
错误! 找不到在keys.dat 件发件人的地址 ( 您的地址).
-
+
做必要的工作, 以发送广播...
-
+
广播发送%1
-
+
加密密钥已请求.
-
+
发送收件人的加密密钥的请求.
-
+
展望接收方的公钥
-
+
问题: 目标是移动电话设备所请求的目的地包括在消息中, 但是这是在你的设置禁止. %1
-
+
做必要的工作, 以发送信息.
这样第2版的地址没有难度.
-
+
做必要的工作, 以发送短信.
接收者的要求难度: %1与%2
-
+
问题: 由接收者(%1%2)要求的工作量比您愿意做的工作量來得更困难. %3
-
+
问题: 您正在尝试将信息发送给自己或频道, 但您的加密密钥无法在keys.dat文件中找到. 无法加密信息. %1
-
+
做必要的工作, 以发送信息.
-
+
信息发送. 等待确认. 已发送%1
-
+
做必要的工作以要求加密密钥.
-
+
广播公钥请求. 这个程序将自动重试, 如果他们处于离线状态.
-
+
发送公钥的请求. 等待回复. 请求在%1
@@ -1336,37 +1306,37 @@ Receiver's required difficulty: %1 and %2
UPnP端口映射被删除
-
+
标记全部信息为已读
-
+
确定将所有信息标记为已读吗?
-
+
持续进行必要的工作,以发送广播。
-
+
待传输内容的校验
-
+
%n 待传输内容校验任务
-
+
%n 任务等待分配
-
+
等待所有任务执行完?
@@ -1411,37 +1381,32 @@ Receiver's required difficulty: %1 and %2
名字%1没有关联比特信地址。
-
+
成功!域名币系统%1运行中。
-
+
成功!域名币控制上线运行!
-
+
不能理解 NMControl。
-
-
-
- 连接到 Namecoin 失败。
-
- 你的GPU不能够正确计算,正在关闭OpenGL。请报告给开发者。
+ 你的GPU(s)不能够正确计算,关闭OpenGL。请报告给开发者。
-
+
设置通知提示音...
-
+
欢迎使用简便安全的比特信
*发送信息给其他人
*像推特那样发送广播信息
-*在频道里和其他人讨论
+*在频道(s)里和其他人讨论
-
+
频道内不建议的内容
-
+
静默模式
-
+
连接问题?请尝试在网络设置里打开UPnP
-
+
您将要尝试经由 Bitmessage 发送一封电子邮件。该操作需要在一个网关上注册。尝试注册?
-
+
错误:Bitmessage地址是以BM-开头的,请检查收信地址%1.
-
+
错误:收信地址%1未填写或复制错误。请检查。
-
+
错误:收信地址%1还有非法字符。请检查。
-
+
错误:收信地址%1版本太高。要么你需要更新你的软件,要么对方需要降级 。
-
+
错误:收信地址%1编码数据太短。可能对方使用的软件有问题。
-
+
错误:
-
+
错误:收信地址%1编码数据太长。可能对方使用的软件有问题。
-
+
错误:收信地址%1有问题。
-
+
错误:%1
-
+
来自 %1
-
+
待同步
-
+
Bitmessage还没有与网络同步,%n 件任务需要下载。如果你现在退出软件,可能会造成传输延时。是否等同步完成?
-
+
未连接成功。
-
+
Bitmessage未连接到网络。如果现在退出软件,可能会造成传输延时。是否等待同步完成?
-
+
等待网络连接……
-
+
等待同步完成……
-
+
- 您已经为该地址簿条目设置了通知提示音。您想要覆盖它吗?
-
-
-
-
- 发生错误:无法从磁盘读取消息。
-
-
-
-
- 显示从此地址最近 %n 的广播。
-
-
-
-
- 上线
-
-
-
-
- 下线
-
-
-
-
- 清除
-
-
-
-
- 收件箱
-
-
-
-
- 新信息
-
-
-
-
- 已发送
-
-
-
-
- 垃圾箱
+
MessageView
-
+
查看外部链接
-
+
此链接“%1”将在浏览器中打开。可能会有安全风险,可能会暴露你或下载恶意数据。确定吗?
-
+
检测到HTML,单击此处来显示内容。
-
+
单击此处以禁止HTML。
@@ -1631,14 +1551,14 @@ Receiver's required difficulty: %1 and %2
MsgDecode
-
+
这些消息使用了未知编码方式。
你可能需要更新Bitmessage软件。
-
+
未知编码
@@ -1646,98 +1566,98 @@ Perhaps you should upgrade Bitmessage.
NewAddressDialog
-
+
创建新地址
-
+
在这里,您想创建多少地址就创建多少。诚然,创建和丢弃地址受到鼓励。你既可以使用随机数来创建地址,也可以使用密钥。如果您使用密钥的话,生成的地址叫“静态地址”。随机数选项默认为选择,不过相比而言静态地址既有缺点也有优点:
-
+
<html><head/><body><p><span style=" font-weight:600;">优点:<br/></span>您可以通过记忆在任何电脑再次得到您的地址. <br/>您不需要注意备份您的 keys.dat 只要您能记住您的密钥。 <br/><span style=" font-weight:600;">缺点:<br/></span>您若要再次得到您的地址,您必须牢记(或写下您的密钥)。 <br/>您必须牢记密钥的同时也牢记地址版本号和the stream number . <br/>如果您选择了一个弱的密钥的话,一些在互联网我那个的人可能有机会暴力破解, 他们将可以阅读您的消息并且以您的身份发送消息.</p></body></html>
-
+
使用随机数生成地址
-
+
使用密钥生成地址
-
+
花费数分钟的计算使地址短1-2个字母
-
+
创建静态地址
-
+
地址版本号:4
-
+
在记住您的密钥的同时,您还需要记住以下数字:
-
+
密钥
-
+
使用该密钥生成的地址数:
-
+
节点流序号:1
-
+
再次输入密钥
-
+
随机生成地址
-
+
标签(只有您看的到)
-
+
使用最可用的节点流
-
+
如果这是您创建的数个地址中的第一个时最佳
-
+
使用和如下地址一样的节点流
-
+
(节省你的带宽和处理能力)
@@ -1745,83 +1665,78 @@ The 'Random Number' option is selected by default but deterministic ad
NewSubscriptionDialog
-
+
添加新条目
-
+
标签
-
+
地址
-
+
- 请在上方键入地址。
+ 输入上述地址.
SpecialAddressBehaviorDialog
-
+
特别的地址行为
-
+
作为普通地址
-
+
作为伪邮件列表地址
-
+
伪邮件列表收到消息时会自动将其公开的广播给订阅者。
-
+
伪邮件列表名称:
-
-
-
- 这是一个频道地址,您无法把它作为伪邮件列表。
-
aboutDialog
-
+
关于
-
-
-
-
-
-
-
+
+ PyBitmessage
-
+
+
+ 版本 ?
+
+
+
<html><head/><body><p>以 MIT/X11 软件授权发布; 详情参见 <a href="http://www.opensource.org/licenses/mit-license.php"><span style=" text-decoration: underline; color:#0000ff;">http://www.opensource.org/licenses/mit-license.php</span></a></p></body></html>
-
+
本软件处于Beta阶段。
@@ -1830,10 +1745,10 @@ The 'Random Number' option is selected by default but deterministic ad
-
-
-
- <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2013-2017 The Bitmessage Developers</p></body></html>
+
+
+
+ <html><head/><body><p>版权:2012-2016 Jonathan Warren<br/>版权: 2013-2016 The Bitmessage Developers</p></body></html>
@@ -1877,45 +1792,40 @@ The 'Random Number' option is selected by default but deterministic ad
connectDialog
-
+
比特信
-
+
除非您允许,比特信不会连接到任何人。
-
+
现在连接
-
+
请先让我进行特别的网络设置
-
-
-
- 离线模式下工作
-
helpDialog
-
+
帮助
-
+
<a href="https://bitmessage.org/wiki/PyBitmessage_Help">https://bitmessage.org/wiki/PyBitmessage_Help</a>
-
+
鉴于比特信是一个共同完成的项目,您可以在比特信的Wiki上了解如何帮助比特信:
@@ -1923,35 +1833,30 @@ The 'Random Number' option is selected by default but deterministic ad
iconGlossaryDialog
-
+
图标含义
-
+
您没有和其他节点的连接.
-
+
你有至少一个到其他节点的出站连接,但是尚未收到入站连接。您的防火墙或路由器可能尚未设置转发入站TCP连接到您的电脑。比特信将正常运行,不过如果您允许入站连接的话将帮助比特信网络并成为一个通信状态更好的节点。
-
+ 您正在使用TCP端口 ? 。(可以在设置中更改).
-
+
您有和其他节点的连接且您的防火墙已经正确配置。
-
-
-
- 您正在使用TCP端口 %1 。(可以在设置中修改)。
-
networkstatus
@@ -2001,27 +1906,27 @@ The 'Random Number' option is selected by default but deterministic ad
-
+
自从%1启动
-
+
下: %1/秒 总计: %2
-
+
上: %1/秒 总计: %2
-
+
总的连接数: %1
-
+
每秒库存查询: %1
@@ -2036,32 +1941,32 @@ The 'Random Number' option is selected by default but deterministic ad
下载: 0 kB /秒
-
+
网络状态
-
+
字节
-
+
要同步的对象: %n
-
+
处理%n人对人的信息.
-
+
处理%n广播信息.
-
+
处理%n公钥.
@@ -2166,7 +2071,7 @@ The 'Random Number' option is selected by default but deterministic ad
- 通道密码/名称:
+
@@ -2192,12 +2097,12 @@ The 'Random Number' option is selected by default but deterministic ad
成功创建或加入频道%1
-
+
频道创建或加入失败
-
+
频道创建或加入已取消
@@ -2231,52 +2136,52 @@ The 'Random Number' option is selected by default but deterministic ad
regenerateAddressesDialog
-
+
重新生成已经存在的地址
-
+
重新生成已经存在的地址
-
+
密钥
-
+
- 使用该密钥生成的地址数:
+ 您想要要使用这个密钥生成的地址数:
-
+
地址版本号:
-
+
节点流序号:
-
+
1
-
+
花费数分钟的计算使地址短1-2个字母
-
+
这个选项需要和您第一次生成的时候相同。
-
+
如果您之前创建了静态地址,但是因为一些意外失去了它们(比如硬盘坏了),您可以在这里将他们再次生成。如果您使用随机数来生成的地址的话,那么这个表格对您没有帮助。
diff --git a/src/upnp.py b/src/upnp.py
index 46d55956..ad72560c 100644
--- a/src/upnp.py
+++ b/src/upnp.py
@@ -224,7 +224,7 @@ class uPnPThread(threading.Thread, StoppableThread):
continue
newRouter = Router(resp, ip)
for router in self.routers:
- if router.routerPath == newRouter.routerPath:
+ if router.location == newRouter.location:
break
else:
logger.debug("Found UPnP router at %s", ip)