diff --git a/COPYING b/COPYING index 547f5487..2ba2d65e 100644 --- a/COPYING +++ b/COPYING @@ -1,5 +1,5 @@ -Copyright (c) 2012-2013 Jonathan Warren -Copyright (c) 2013 The Bitmessage Developers +Copyright (c) 2012-2014 Jonathan Warren +Copyright (c) 2013-2014 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 diff --git a/INSTALL.md b/INSTALL.md index 4eb896eb..0d7b6c61 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -29,7 +29,7 @@ git fetch --all git reset --hard origin/master python bitmessagemain.py ``` -Viola! Bitmessage is updated! +Voilà! Bitmessage is updated! ####Linux To run PyBitmessage from the command-line, you must download the source, then diff --git a/LICENSE b/LICENSE index cac11fcf..56ac8e3a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2012-2013 Jonathan Warren -Copyright (c) 2013 The Bitmessage Developers +Copyright (c) 2012-2014 Jonathan Warren +Copyright (c) 2013-2014 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/Makefile b/Makefile index e0375964..457bd128 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ APP=pybitmessage -VERSION=0.4.1 +VERSION=0.4.2 RELEASE=1 ARCH_TYPE=`uname -m` PREFIX?=/usr/local @@ -28,6 +28,7 @@ install: mkdir -m 755 -p ${DESTDIR}${PREFIX}/share/icons/hicolor/24x24/apps install -m 644 desktop/${APP}.desktop ${DESTDIR}${PREFIX}/share/applications/${APP}.desktop install -m 644 desktop/icon24.png ${DESTDIR}${PREFIX}/share/icons/hicolor/24x24/apps/${APP}.png + install -m 644 desktop/can-icon.svg ${DESTDIR}${PREFIX}/share/icons/hicolor/scalable/apps/${APP}.svg cp -rf src/* ${DESTDIR}${PREFIX}/share/${APP} echo '#!/bin/sh' > ${DESTDIR}${PREFIX}/bin/${APP} echo 'if [ -d ${DESTDIR}/usr/local/share/${APP} ]; then' >> ${DESTDIR}${PREFIX}/bin/${APP} diff --git a/arch.sh b/arch.sh index 386974e7..e025ddcf 100755 --- a/arch.sh +++ b/arch.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.4.1 -VERSION=0.4.1 +PREV_VERSION=0.4.2 +VERSION=0.4.2 RELEASE=1 ARCH_TYPE=any CURRDIR=`pwd` diff --git a/archpackage/PKGBUILD b/archpackage/PKGBUILD index 76657030..33eed270 100644 --- a/archpackage/PKGBUILD +++ b/archpackage/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Bob Mottram (4096 bits) pkgname=pybitmessage -pkgver=0.4.1 +pkgver=0.4.2 pkgrel=1 pkgdesc="Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide "non-content" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." arch=('any') diff --git a/debian.sh b/debian.sh index c279d07e..d80d9db9 100755 --- a/debian.sh +++ b/debian.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.4.1 -VERSION=0.4.1 +PREV_VERSION=0.4.2 +VERSION=0.4.2 RELEASE=1 ARCH_TYPE=all DIR=${APP}-${VERSION} diff --git a/ebuild.sh b/ebuild.sh index c2beffb1..59295718 100755 --- a/ebuild.sh +++ b/ebuild.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.4.1 -VERSION=0.4.1 +PREV_VERSION=0.4.2 +VERSION=0.4.2 RELEASE=1 SOURCEDIR=. ARCH_TYPE=`uname -m` diff --git a/generate.sh b/generate.sh index 00aad16e..933c08e9 100755 --- a/generate.sh +++ b/generate.sh @@ -4,7 +4,7 @@ rm -f Makefile rpmpackage/*.spec -packagemonkey -n "PyBitmessage" --version "0.4.1" --dir "." -l "mit" \ +packagemonkey -n "PyBitmessage" --version "0.4.2" --dir "." -l "mit" \ -e "Bob Mottram (4096 bits) " \ --brief "Send encrypted messages" \ --desc "Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide \"non-content\" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." \ diff --git a/puppy.sh b/puppy.sh index 549c1200..6107ff4a 100755 --- a/puppy.sh +++ b/puppy.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.4.1 -VERSION=0.4.1 +PREV_VERSION=0.4.2 +VERSION=0.4.2 RELEASE=1 BUILDDIR=~/petbuild CURRDIR=`pwd` diff --git a/rpm.sh b/rpm.sh index 7595a500..074a4515 100755 --- a/rpm.sh +++ b/rpm.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.4.1 -VERSION=0.4.1 +PREV_VERSION=0.4.2 +VERSION=0.4.2 RELEASE=1 SOURCEDIR=. ARCH_TYPE=`uname -m` diff --git a/rpmpackage/pybitmessage.spec b/rpmpackage/pybitmessage.spec index 67332f63..a33fac72 100644 --- a/rpmpackage/pybitmessage.spec +++ b/rpmpackage/pybitmessage.spec @@ -1,5 +1,5 @@ Name: pybitmessage -Version: 0.4.1 +Version: 0.4.2 Release: 1%{?dist} Summary: Send encrypted messages License: MIT diff --git a/slack.sh b/slack.sh index 7a6013a3..9b6d70f7 100755 --- a/slack.sh +++ b/slack.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.4.1 -VERSION=0.4.1 +PREV_VERSION=0.4.2 +VERSION=0.4.2 RELEASE=1 ARCH_TYPE=`uname -m` BUILDDIR=~/slackbuild diff --git a/src/api.py b/src/api.py new file mode 100644 index 00000000..bd28d923 --- /dev/null +++ b/src/api.py @@ -0,0 +1,912 @@ +# Copyright (c) 2012-2014 Jonathan Warren +# Copyright (c) 2012-2014 The Bitmessage developers + +comment= """ +This is not what you run to run the Bitmessage API. Instead, enable the API +( https://bitmessage.org/wiki/API ) and optionally enable daemon mode +( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py. +""" + +if __name__ == "__main__": + print comment + import sys + sys.exit(0) + +from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler +import json + +import shared +import time +from addresses import decodeAddress,addBMIfNotPresent,decodeVarint,calculateInventoryHash +import helper_inbox +import helper_sent +import hashlib + +from pyelliptic.openssl import OpenSSL +from struct import pack + +# Classes +from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute +from debug import logger + +# Helper Functions +import proofofwork + +str_chan = '[chan]' + + +class APIError(Exception): + def __init__(self, error_number, error_message): + super(APIError, self).__init__() + self.error_number = error_number + self.error_message = error_message + def __str__(self): + return "API Error %04i: %s" % (self.error_number, self.error_message) + +# This is one of several classes that constitute the API +# This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros). +# http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/ +class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): + + def do_POST(self): + # Handles the HTTP POST request. + # Attempts to interpret all HTTP POST requests as XML-RPC calls, + # which are forwarded to the server's _dispatch method for handling. + + # Note: this method is the same as in SimpleXMLRPCRequestHandler, + # just hacked to handle cookies + + # Check that the path is legal + if not self.is_rpc_path_valid(): + self.report_404() + return + + try: + # Get arguments by reading body of request. + # We read this in chunks to avoid straining + # socket.read(); around the 10 or 15Mb mark, some platforms + # begin to have problems (bug #792570). + max_chunk_size = 10 * 1024 * 1024 + size_remaining = int(self.headers["content-length"]) + L = [] + while size_remaining: + chunk_size = min(size_remaining, max_chunk_size) + L.append(self.rfile.read(chunk_size)) + size_remaining -= len(L[-1]) + data = ''.join(L) + + # In previous versions of SimpleXMLRPCServer, _dispatch + # could be overridden in this class, instead of in + # SimpleXMLRPCDispatcher. To maintain backwards compatibility, + # check to see if a subclass implements _dispatch and dispatch + # using that method if present. + response = self.server._marshaled_dispatch( + data, getattr(self, '_dispatch', None) + ) + except: # This should only happen if the module is buggy + # internal error, report as HTTP server error + self.send_response(500) + self.end_headers() + else: + # got a valid XML RPC response + self.send_response(200) + self.send_header("Content-type", "text/xml") + self.send_header("Content-length", str(len(response))) + + # HACK :start -> sends cookies here + if self.cookies: + for cookie in self.cookies: + self.send_header('Set-Cookie', cookie.output(header='')) + # HACK :end + + self.end_headers() + self.wfile.write(response) + + # shut down the connection + self.wfile.flush() + self.connection.shutdown(1) + + def APIAuthenticateClient(self): + if 'Authorization' in self.headers: + # handle Basic authentication + (enctype, encstr) = self.headers.get('Authorization').split() + (emailid, password) = encstr.decode('base64').split(':') + if emailid == shared.config.get('bitmessagesettings', 'apiusername') and password == shared.config.get('bitmessagesettings', 'apipassword'): + return True + else: + return False + else: + logger.warn('Authentication failed because header lacks Authentication field') + time.sleep(2) + return False + + return False + + def _decode(self, text, decode_type): + try: + return text.decode(decode_type) + except Exception as e: + raise APIError(22, "Decode error - " + str(e) + ". Had trouble while decoding string: " + repr(text)) + + def _verifyAddress(self, address): + status, addressVersionNumber, streamNumber, ripe = decodeAddress(address) + if status != 'success': + logger.warn('API Error 0007: Could not decode address %s. Status: %s.', address, status) + + if status == 'checksumfailed': + raise APIError(8, 'Checksum failed for address: ' + address) + if status == 'invalidcharacters': + raise APIError(9, 'Invalid characters in address: ' + address) + if status == 'versiontoohigh': + raise APIError(10, 'Address version number too high (or zero) in address: ' + address) + raise APIError(7, 'Could not decode address: ' + address + ' : ' + status) + if addressVersionNumber < 2 or addressVersionNumber > 4: + raise APIError(11, 'The address version number currently must be 2, 3 or 4. Others aren\'t supported. Check the address.') + if streamNumber != 1: + raise APIError(12, 'The stream number must be 1. Others aren\'t supported. Check the address.') + + return (status, addressVersionNumber, streamNumber, ripe) + + def _handle_request(self, method, params): + if method == 'helloWorld': + (a, b) = params + return a + '-' + b + elif method == 'add': + (a, b) = params + return a + b + elif method == 'statusBar': + message, = params + shared.UISignalQueue.put(('updateStatusBar', message)) + elif method == 'listAddresses' or method == 'listAddresses2': + data = '{"addresses":[' + configSections = shared.config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile != 'bitmessagesettings': + status, addressVersionNumber, streamNumber, hash01 = decodeAddress( + addressInKeysFile) + if len(data) > 20: + data += ',' + if shared.config.has_option(addressInKeysFile, 'chan'): + chan = shared.config.getboolean(addressInKeysFile, 'chan') + else: + chan = False + label = shared.config.get(addressInKeysFile, 'label') + if method == 'listAddresses2': + label = label.encode('base64') + data += json.dumps({'label': label, 'address': addressInKeysFile, 'stream': + streamNumber, 'enabled': shared.config.getboolean(addressInKeysFile, 'enabled'), 'chan': chan}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'listAddressBookEntries' or method == 'listAddressbook': # the listAddressbook alias should be removed eventually. + queryreturn = sqlQuery('''SELECT label, address from addressbook''') + data = '{"addresses":[' + for row in queryreturn: + label, address = row + label = shared.fixPotentiallyInvalidUTF8Data(label) + if len(data) > 20: + data += ',' + data += json.dumps({'label':label.encode('base64'), 'address': address}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'addAddressBookEntry' or method == 'addAddressbook': # the addAddressbook alias should be deleted eventually. + if len(params) != 2: + raise APIError(0, "I need label and address") + address, label = params + label = self._decode(label, "base64") + address = addBMIfNotPresent(address) + self._verifyAddress(address) + queryreturn = sqlQuery("SELECT address FROM addressbook WHERE address=?", address) + if queryreturn != []: + raise APIError(16, 'You already have this address in your address book.') + + sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.UISignalQueue.put(('rerenderAddressBook','')) + return "Added address %s to address book" % address + elif method == 'deleteAddressBookEntry' or method == 'deleteAddressbook': # The deleteAddressbook alias should be deleted eventually. + if len(params) != 1: + raise APIError(0, "I need an address") + address, = params + address = addBMIfNotPresent(address) + self._verifyAddress(address) + sqlExecute('DELETE FROM addressbook WHERE address=?', address) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.UISignalQueue.put(('rerenderAddressBook','')) + return "Deleted address book entry for %s if it existed" % address + elif method == 'createRandomAddress': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + elif len(params) == 1: + label, = params + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 2: + label, eighteenByteRipe = params + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 3: + label, eighteenByteRipe, totalDifficulty = params + nonceTrialsPerByte = int( + shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 4: + label, eighteenByteRipe, totalDifficulty, smallMessageDifficulty = params + nonceTrialsPerByte = int( + shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) + payloadLengthExtraBytes = int( + shared.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) + else: + raise APIError(0, 'Too many parameters!') + label = self._decode(label, "base64") + try: + unicode(label, 'utf-8') + except: + raise APIError(17, 'Label is not valid UTF-8 data.') + shared.apiAddressGeneratorReturnQueue.queue.clear() + streamNumberForAddress = 1 + shared.addressGeneratorQueue.put(( + 'createRandomAddress', 4, streamNumberForAddress, label, 1, "", eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes)) + return shared.apiAddressGeneratorReturnQueue.get() + elif method == 'createDeterministicAddresses': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + elif len(params) == 1: + passphrase, = params + numberOfAddresses = 1 + addressVersionNumber = 0 + streamNumber = 0 + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 2: + passphrase, numberOfAddresses = params + addressVersionNumber = 0 + streamNumber = 0 + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 3: + passphrase, numberOfAddresses, addressVersionNumber = params + streamNumber = 0 + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 4: + passphrase, numberOfAddresses, addressVersionNumber, streamNumber = params + eighteenByteRipe = False + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 5: + passphrase, numberOfAddresses, addressVersionNumber, streamNumber, eighteenByteRipe = params + nonceTrialsPerByte = shared.config.get( + 'bitmessagesettings', 'defaultnoncetrialsperbyte') + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 6: + passphrase, numberOfAddresses, addressVersionNumber, streamNumber, eighteenByteRipe, totalDifficulty = params + nonceTrialsPerByte = int( + shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) + payloadLengthExtraBytes = shared.config.get( + 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 7: + passphrase, numberOfAddresses, addressVersionNumber, streamNumber, eighteenByteRipe, totalDifficulty, smallMessageDifficulty = params + nonceTrialsPerByte = int( + shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) + payloadLengthExtraBytes = int( + shared.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) + else: + raise APIError(0, 'Too many parameters!') + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + if not isinstance(eighteenByteRipe, bool): + raise APIError(23, 'Bool expected in eighteenByteRipe, saw %s instead' % type(eighteenByteRipe)) + passphrase = self._decode(passphrase, "base64") + if addressVersionNumber == 0: # 0 means "just use the proper addressVersionNumber" + addressVersionNumber = 4 + if addressVersionNumber != 3 and addressVersionNumber != 4: + raise APIError(2,'The address version number currently must be 3, 4, or 0 (which means auto-select). ' + addressVersionNumber + ' isn\'t supported.') + if streamNumber == 0: # 0 means "just use the most available stream" + streamNumber = 1 + if streamNumber != 1: + raise APIError(3,'The stream number must be 1 (or 0 which means auto-select). Others aren\'t supported.') + if numberOfAddresses == 0: + raise APIError(4, 'Why would you ask me to generate 0 addresses for you?') + if numberOfAddresses > 999: + raise APIError(5, 'You have (accidentally?) specified too many addresses to make. Maximum 999. This check only exists to prevent mischief; if you really want to create more addresses than this, contact the Bitmessage developers and we can modify the check or you can do it yourself by searching the source code for this message.') + shared.apiAddressGeneratorReturnQueue.queue.clear() + logger.debug('Requesting that the addressGenerator create %s addresses.', numberOfAddresses) + shared.addressGeneratorQueue.put( + ('createDeterministicAddresses', addressVersionNumber, streamNumber, + 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes)) + data = '{"addresses":[' + queueReturn = shared.apiAddressGeneratorReturnQueue.get() + for item in queueReturn: + if len(data) > 20: + data += ',' + data += "\"" + item + "\"" + data += ']}' + return data + elif method == 'getDeterministicAddress': + if len(params) != 3: + raise APIError(0, 'I need exactly 3 parameters.') + passphrase, addressVersionNumber, streamNumber = params + numberOfAddresses = 1 + eighteenByteRipe = False + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + passphrase = self._decode(passphrase, "base64") + if addressVersionNumber != 3 and addressVersionNumber != 4: + raise APIError(2, 'The address version number currently must be 3 or 4. ' + addressVersionNumber + ' isn\'t supported.') + if streamNumber != 1: + raise APIError(3, ' The stream number must be 1. Others aren\'t supported.') + shared.apiAddressGeneratorReturnQueue.queue.clear() + logger.debug('Requesting that the addressGenerator create %s addresses.', numberOfAddresses) + shared.addressGeneratorQueue.put( + ('getDeterministicAddress', addressVersionNumber, + streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe)) + return shared.apiAddressGeneratorReturnQueue.get() + + elif method == 'createChan': + if len(params) == 0: + raise APIError(0, 'I need parameters.') + elif len(params) == 1: + passphrase, = params + passphrase = self._decode(passphrase, "base64") + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + # It would be nice to make the label the passphrase but it is + # possible that the passphrase contains non-utf-8 characters. + try: + unicode(passphrase, 'utf-8') + label = str_chan + ' ' + passphrase + except: + label = str_chan + ' ' + repr(passphrase) + + addressVersionNumber = 4 + streamNumber = 1 + shared.apiAddressGeneratorReturnQueue.queue.clear() + logger.debug('Requesting that the addressGenerator create chan %s.', passphrase) + shared.addressGeneratorQueue.put(('createChan', addressVersionNumber, streamNumber, label, passphrase)) + queueReturn = shared.apiAddressGeneratorReturnQueue.get() + if len(queueReturn) == 0: + raise APIError(24, 'Chan address is already present.') + address = queueReturn[0] + return address + elif method == 'joinChan': + if len(params) < 2: + raise APIError(0, 'I need two parameters.') + elif len(params) == 2: + passphrase, suppliedAddress= params + passphrase = self._decode(passphrase, "base64") + if len(passphrase) == 0: + raise APIError(1, 'The specified passphrase is blank.') + # It would be nice to make the label the passphrase but it is + # possible that the passphrase contains non-utf-8 characters. + try: + unicode(passphrase, 'utf-8') + label = str_chan + ' ' + passphrase + except: + label = str_chan + ' ' + repr(passphrase) + + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(suppliedAddress) + suppliedAddress = addBMIfNotPresent(suppliedAddress) + shared.apiAddressGeneratorReturnQueue.queue.clear() + shared.addressGeneratorQueue.put(('joinChan', suppliedAddress, label, passphrase)) + addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() + + if addressGeneratorReturnValue == 'chan name does not match address': + raise APIError(18, 'Chan name does not match address.') + if len(addressGeneratorReturnValue) == 0: + raise APIError(24, 'Chan address is already present.') + #TODO: this variable is not used to anything + createdAddress = addressGeneratorReturnValue[0] # in case we ever want it for anything. + return "success" + elif method == 'leaveChan': + if len(params) == 0: + raise APIError(0, 'I need parameters.') + elif len(params) == 1: + address, = params + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) + address = addBMIfNotPresent(address) + if not shared.config.has_section(address): + raise APIError(13, 'Could not find this address in your keys.dat file.') + if not shared.safeConfigGetBoolean(address, 'chan'): + raise APIError(25, 'Specified address is not a chan address. Use deleteAddress API call instead.') + shared.config.remove_section(address) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + return 'success' + + elif method == 'deleteAddress': + if len(params) == 0: + raise APIError(0, 'I need parameters.') + elif len(params) == 1: + address, = params + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) + address = addBMIfNotPresent(address) + if not shared.config.has_section(address): + raise APIError(13, 'Could not find this address in your keys.dat file.') + shared.config.remove_section(address) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + shared.UISignalQueue.put(('rerenderInboxFromLabels','')) + shared.UISignalQueue.put(('rerenderSentToLabels','')) + shared.reloadMyAddressHashes() + return 'success' + + elif method == 'getAllInboxMessages': + queryreturn = sqlQuery( + '''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox where folder='inbox' ORDER BY received''') + data = '{"inboxMessages":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + if len(data) > 25: + data += ',' + data += json.dumps({'msgid': msgid.encode('hex'), 'toAddress': toAddress, 'fromAddress': fromAddress, 'subject': subject.encode( + 'base64'), 'message': message.encode('base64'), 'encodingType': encodingtype, 'receivedTime': received, 'read': read}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getAllInboxMessageIds' or method == 'getAllInboxMessageIDs': + queryreturn = sqlQuery( + '''SELECT msgid FROM inbox where folder='inbox' ORDER BY received''') + data = '{"inboxMessageIds":[' + for row in queryreturn: + msgid = row[0] + if len(data) > 25: + data += ',' + data += json.dumps({'msgid': msgid.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getInboxMessageById' or method == 'getInboxMessageByID': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + elif len(params) == 1: + msgid = self._decode(params[0], "hex") + elif len(params) >= 2: + msgid = self._decode(params[0], "hex") + readStatus = params[1] + if not isinstance(readStatus, bool): + raise APIError(23, 'Bool expected in readStatus, saw %s instead.' % type(readStatus)) + queryreturn = sqlQuery('''SELECT read FROM inbox WHERE msgid=?''', msgid) + # UPDATE is slow, only update if status is different + if queryreturn != [] and (queryreturn[0][0] == 1) != readStatus: + sqlExecute('''UPDATE inbox set read = ? WHERE msgid=?''', readStatus, msgid) + shared.UISignalQueue.put(('changedInboxUnread', None)) + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox WHERE msgid=?''', msgid) + data = '{"inboxMessage":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received, 'read': read}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getAllSentMessages': + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent where folder='sent' ORDER BY lastactiontime''') + data = '{"sentMessages":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getAllSentMessageIds' or method == 'getAllSentMessageIDs': + queryreturn = sqlQuery('''SELECT msgid FROM sent where folder='sent' ORDER BY lastactiontime''') + data = '{"sentMessageIds":[' + for row in queryreturn: + msgid = row[0] + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getInboxMessagesByReceiver' or method == 'getInboxMessagesByAddress': #after some time getInboxMessagesByAddress should be removed + if len(params) == 0: + raise APIError(0, 'I need parameters!') + toAddress = params[0] + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype FROM inbox WHERE folder='inbox' AND toAddress=?''', toAddress) + data = '{"inboxMessages":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, received, message, encodingtype = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getSentMessageById' or method == 'getSentMessageByID': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE msgid=?''', msgid) + data = '{"sentMessage":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getSentMessagesByAddress' or method == 'getSentMessagesBySender': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + fromAddress = params[0] + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE folder='sent' AND fromAddress=? ORDER BY lastactiontime''', + fromAddress) + data = '{"sentMessages":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getSentMessageByAckData': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + ackData = self._decode(params[0], "hex") + queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE ackdata=?''', + ackData) + data = '{"sentMessage":[' + for row in queryreturn: + msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) + data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'trashMessage': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") + + # Trash if in inbox table + helper_inbox.trash(msgid) + # Trash if in sent table + sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) + return 'Trashed message (assuming message existed).' + elif method == 'trashInboxMessage': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") + helper_inbox.trash(msgid) + return 'Trashed inbox message (assuming message existed).' + elif method == 'trashSentMessage': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + msgid = self._decode(params[0], "hex") + sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) + return 'Trashed sent message (assuming message existed).' + elif method == 'trashSentMessageByAckData': + # This API method should only be used when msgid is not available + if len(params) == 0: + raise APIError(0, 'I need parameters!') + ackdata = self._decode(params[0], "hex") + sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', + ackdata) + return 'Trashed sent message (assuming message existed).' + elif method == 'sendMessage': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + elif len(params) == 4: + toAddress, fromAddress, subject, message = params + encodingType = 2 + elif len(params) == 5: + toAddress, fromAddress, subject, message, encodingType = params + if encodingType != 2: + raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') + subject = self._decode(subject, "base64") + message = self._decode(message, "base64") + toAddress = addBMIfNotPresent(toAddress) + fromAddress = addBMIfNotPresent(fromAddress) + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(toAddress) + self._verifyAddress(fromAddress) + try: + fromAddressEnabled = shared.config.getboolean( + fromAddress, 'enabled') + except: + raise APIError(13, 'Could not find your fromAddress in the keys.dat file.') + if not fromAddressEnabled: + raise APIError(14, 'Your fromAddress is disabled. Cannot send.') + + ackdata = OpenSSL.rand(32) + + t = ('', toAddress, toRipe, fromAddress, subject, message, ackdata, int( + time.time()), 'msgqueued', 1, 1, 'sent', 2) + helper_sent.insert(t) + + toLabel = '' + queryreturn = sqlQuery('''select label from addressbook where address=?''', toAddress) + if queryreturn != []: + for row in queryreturn: + toLabel, = row + # apiSignalQueue.put(('displayNewSentMessage',(toAddress,toLabel,fromAddress,subject,message,ackdata))) + shared.UISignalQueue.put(('displayNewSentMessage', ( + toAddress, toLabel, fromAddress, subject, message, ackdata))) + + shared.workerQueue.put(('sendmessage', toAddress)) + + return ackdata.encode('hex') + + elif method == 'sendBroadcast': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + if len(params) == 3: + fromAddress, subject, message = params + encodingType = 2 + elif len(params) == 4: + fromAddress, subject, message, encodingType = params + if encodingType != 2: + raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') + subject = self._decode(subject, "base64") + message = self._decode(message, "base64") + + fromAddress = addBMIfNotPresent(fromAddress) + self._verifyAddress(fromAddress) + try: + fromAddressEnabled = shared.config.getboolean( + fromAddress, 'enabled') + except: + raise APIError(13, 'could not find your fromAddress in the keys.dat file.') + ackdata = OpenSSL.rand(32) + toAddress = '[Broadcast subscribers]' + ripe = '' + + + t = ('', toAddress, ripe, fromAddress, subject, message, ackdata, int( + time.time()), 'broadcastqueued', 1, 1, 'sent', 2) + helper_sent.insert(t) + + toLabel = '[Broadcast subscribers]' + shared.UISignalQueue.put(('displayNewSentMessage', ( + toAddress, toLabel, fromAddress, subject, message, ackdata))) + shared.workerQueue.put(('sendbroadcast', '')) + + return ackdata.encode('hex') + elif method == 'getStatus': + if len(params) != 1: + raise APIError(0, 'I need one parameter!') + ackdata, = params + if len(ackdata) != 64: + raise APIError(15, 'The length of ackData should be 32 bytes (encoded in hex thus 64 characters).') + ackdata = self._decode(ackdata, "hex") + queryreturn = sqlQuery( + '''SELECT status FROM sent where ackdata=?''', + ackdata) + if queryreturn == []: + return 'notfound' + for row in queryreturn: + status, = row + return status + elif method == 'addSubscription': + if len(params) == 0: + raise APIError(0, 'I need parameters!') + if len(params) == 1: + address, = params + label = '' + if len(params) == 2: + address, label = params + label = self._decode(label, "base64") + try: + unicode(label, 'utf-8') + except: + raise APIError(17, 'Label is not valid UTF-8 data.') + if len(params) > 2: + raise APIError(0, 'I need either 1 or 2 parameters!') + address = addBMIfNotPresent(address) + self._verifyAddress(address) + # First we must check to see if the address is already in the + # subscriptions list. + queryreturn = sqlQuery('''select * from subscriptions where address=?''', address) + if queryreturn != []: + raise APIError(16, 'You are already subscribed to that address.') + sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',label, address, True) + shared.reloadBroadcastSendersForWhichImWatching() + shared.UISignalQueue.put(('rerenderInboxFromLabels', '')) + shared.UISignalQueue.put(('rerenderSubscriptions', '')) + return 'Added subscription.' + + elif method == 'deleteSubscription': + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + address, = params + address = addBMIfNotPresent(address) + sqlExecute('''DELETE FROM subscriptions WHERE address=?''', address) + shared.reloadBroadcastSendersForWhichImWatching() + shared.UISignalQueue.put(('rerenderInboxFromLabels', '')) + shared.UISignalQueue.put(('rerenderSubscriptions', '')) + return 'Deleted subscription if it existed.' + elif method == 'listSubscriptions': + queryreturn = sqlQuery('''SELECT label, address, enabled FROM subscriptions''') + data = '{"subscriptions":[' + for row in queryreturn: + label, address, enabled = row + label = shared.fixPotentiallyInvalidUTF8Data(label) + if len(data) > 20: + data += ',' + data += json.dumps({'label':label.encode('base64'), 'address': address, 'enabled': enabled == 1}, indent=4, separators=(',',': ')) + data += ']}' + return data + elif method == 'disseminatePreEncryptedMsg': + # The device issuing this command to PyBitmessage supplies a msg object that has + # already been encrypted but which still needs the POW to be done. PyBitmessage + # accepts this msg object and sends it out to the rest of the Bitmessage network + # as if it had generated the message itself. Please do not yet add this to the + # api doc. + if len(params) != 3: + raise APIError(0, 'I need 3 parameter!') + encryptedPayload, requiredAverageProofOfWorkNonceTrialsPerByte, requiredPayloadLengthExtraBytes = params + encryptedPayload = self._decode(encryptedPayload, "hex") + # Let us do the POW and attach it to the front + target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte) + with shared.printLock: + print '(For msg message via API) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes + powStartTime = time.time() + initialHash = hashlib.sha512(encryptedPayload).digest() + trialValue, nonce = proofofwork.run(target, initialHash) + with shared.printLock: + print '(For msg message via API) Found proof of work', trialValue, 'Nonce:', nonce + try: + print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.' + except: + pass + encryptedPayload = pack('>Q', nonce) + encryptedPayload + toStreamNumber = decodeVarint(encryptedPayload[16:26])[0] + inventoryHash = calculateInventoryHash(encryptedPayload) + objectType = 'msg' + shared.inventory[inventoryHash] = ( + objectType, toStreamNumber, encryptedPayload, int(time.time()),'') + shared.inventorySets[toStreamNumber].add(inventoryHash) + with shared.printLock: + print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex') + shared.broadcastToSendDataQueues(( + toStreamNumber, 'advertiseobject', inventoryHash)) + elif method == 'disseminatePubkey': + # The device issuing this command to PyBitmessage supplies a pubkey object to be + # disseminated to the rest of the Bitmessage network. PyBitmessage accepts this + # pubkey object and sends it out to the rest of the Bitmessage network as if it + # had generated the pubkey object itself. Please do not yet add this to the api + # doc. + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + payload, = params + payload = self._decode(payload, "hex") + + # Let us do the POW + target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes + + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) + print '(For pubkey message via API) Doing proof of work...' + initialHash = hashlib.sha512(payload).digest() + trialValue, nonce = proofofwork.run(target, initialHash) + print '(For pubkey message via API) Found proof of work', trialValue, 'Nonce:', nonce + payload = pack('>Q', nonce) + payload + + pubkeyReadPosition = 8 # bypass the nonce + if payload[pubkeyReadPosition:pubkeyReadPosition+4] == '\x00\x00\x00\x00': # if this pubkey uses 8 byte time + pubkeyReadPosition += 8 + else: + pubkeyReadPosition += 4 + addressVersion, addressVersionLength = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10]) + pubkeyReadPosition += addressVersionLength + pubkeyStreamNumber = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10])[0] + inventoryHash = calculateInventoryHash(payload) + objectType = 'pubkey' + #todo: support v4 pubkeys + shared.inventory[inventoryHash] = ( + objectType, pubkeyStreamNumber, payload, int(time.time()),'') + shared.inventorySets[pubkeyStreamNumber].add(inventoryHash) + with shared.printLock: + print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex') + shared.broadcastToSendDataQueues(( + streamNumber, 'advertiseobject', inventoryHash)) + elif method == 'getMessageDataByDestinationHash' or method == 'getMessageDataByDestinationTag': + # Method will eventually be used by a particular Android app to + # select relevant messages. Do not yet add this to the api + # doc. + + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + requestedHash, = params + if len(requestedHash) != 32: + raise APIError(19, 'The length of hash should be 32 bytes (encoded in hex thus 64 characters).') + requestedHash = self._decode(requestedHash, "hex") + + # This is not a particularly commonly used API function. Before we + # use it we'll need to fill out a field in our inventory database + # which is blank by default (first20bytesofencryptedmessage). + queryreturn = sqlQuery( + '''SELECT hash, payload FROM inventory WHERE tag = '' and objecttype = 'msg' ; ''') + with SqlBulkExecute() as sql: + for row in queryreturn: + hash01, payload = row + readPosition = 16 # Nonce length + time length + readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length + t = (payload[readPosition:readPosition+32],hash01) + sql.execute('''UPDATE inventory SET tag=? WHERE hash=?; ''', *t) + + queryreturn = sqlQuery('''SELECT payload FROM inventory WHERE tag = ?''', + requestedHash) + data = '{"receivedMessageDatas":[' + for row in queryreturn: + payload, = row + if len(data) > 25: + data += ',' + data += json.dumps({'data':payload.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'getPubkeyByHash': + # Method will eventually be used by a particular Android app to + # retrieve pubkeys. Please do not yet add this to the api docs. + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + requestedHash, = params + if len(requestedHash) != 40: + raise APIError(19, 'The length of hash should be 20 bytes (encoded in hex thus 40 characters).') + requestedHash = self._decode(requestedHash, "hex") + queryreturn = sqlQuery('''SELECT transmitdata FROM pubkeys WHERE hash = ? ; ''', requestedHash) + data = '{"pubkey":[' + for row in queryreturn: + transmitdata, = row + data += json.dumps({'data':transmitdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'clientStatus': + if len(shared.connectedHostsList) == 0: + networkStatus = 'notConnected' + elif len(shared.connectedHostsList) > 0 and not shared.clientHasReceivedIncomingConnections: + networkStatus = 'connectedButHaveNotReceivedIncomingConnections' + else: + networkStatus = 'connectedAndReceivingIncomingConnections' + return json.dumps({'networkConnections':len(shared.connectedHostsList),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus, 'softwareName':'PyBitmessage','softwareVersion':shared.softwareVersion}, indent=4, separators=(',', ': ')) + elif method == 'decodeAddress': + # Return a meaningful decoding of an address. + if len(params) != 1: + raise APIError(0, 'I need 1 parameter!') + address, = params + status, addressVersion, streamNumber, ripe = decodeAddress(address) + return json.dumps({'status':status, 'addressVersion':addressVersion, + 'streamNumber':streamNumber, 'ripe':ripe.encode('base64')}, indent=4, + separators=(',', ': ')) + else: + raise APIError(20, 'Invalid method: %s' % method) + + def _dispatch(self, method, params): + self.cookies = [] + + validuser = self.APIAuthenticateClient() + if not validuser: + time.sleep(2) + return "RPC Username or password incorrect or HTTP header lacks authentication at all." + + try: + return self._handle_request(method, params) + except APIError as e: + return str(e) + except Exception as e: + logger.exception(e) + return "API Error 0021: Unexpected API Failure - %s" % str(e) + diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index ab63664d..e4a073d9 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -11,935 +11,68 @@ import signal # Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully. # The next 3 are used for the API -from SimpleXMLRPCServer import * -import json import singleton import os +from SimpleXMLRPCServer import SimpleXMLRPCServer +from api import MySimpleXMLRPCRequestHandler +from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections + +import shared +from helper_sql import sqlQuery +import threading + +# Classes +#from helper_sql import * +#from class_sqlThread import * +from class_sqlThread import sqlThread +from class_singleCleaner import singleCleaner +#from class_singleWorker import * +from class_objectProcessor import objectProcessor +from class_outgoingSynSender import outgoingSynSender +from class_singleListener import singleListener +from class_singleWorker import singleWorker +#from class_addressGenerator import * +from class_addressGenerator import addressGenerator +from debug import logger + +# Helper Functions +import helper_bootstrap +import helper_generic + +from subprocess import call +import time + # OSX python version check import sys if sys.platform == 'darwin': if float("{1}.{2}".format(*sys.version_info)) < 7.5: - print "You should use python 2.7.5 or greater." - print "Your version: {0}.{1}.{2}".format(*sys.version_info) - sys.exit(0) - -# Classes -from helper_sql import * -from class_sqlThread import * -from class_singleCleaner import * -from class_singleWorker import * -from class_outgoingSynSender import * -from class_singleListener import * -from class_addressGenerator import * -from debug import logger - -# Helper Functions -import helper_bootstrap -import proofofwork - -str_chan = '[chan]' - -import sys -if sys.platform == 'darwin': - if float("{1}.{2}".format(*sys.version_info)) < 7.5: - logger.critical("You should use python 2.7.5 or greater. Your version: %s", "{0}.{1}.{2}".format(*sys.version_info)) + msg = "You should use python 2.7.5 or greater. Your version: %s", "{0}.{1}.{2}".format(*sys.version_info) + logger.critical(msg) + print msg sys.exit(0) def connectToStream(streamNumber): + shared.streamsInWhichIAmParticipating[streamNumber] = 'no data' selfInitiatedConnections[streamNumber] = {} shared.inventorySets[streamNumber] = set() queryData = sqlQuery('''SELECT hash FROM inventory WHERE streamnumber=?''', streamNumber) for row in queryData: shared.inventorySets[streamNumber].add(row[0]) - if sys.platform[0:3] == 'win': + + if isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections(): + # Some XP and Vista systems can only have 10 outgoing connections at a time. maximumNumberOfHalfOpenConnections = 9 else: - maximumNumberOfHalfOpenConnections = 32 + maximumNumberOfHalfOpenConnections = 64 for i in range(maximumNumberOfHalfOpenConnections): a = outgoingSynSender() a.setup(streamNumber, selfInitiatedConnections) a.start() -class APIError(Exception): - def __init__(self, error_number, error_message): - self.error_number = error_number - self.error_message = error_message - def __str__(self): - return "API Error %04i: %s" % (self.error_number, self.error_message) - -# This is one of several classes that constitute the API -# This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros). -# http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/ -class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): - - def do_POST(self): - # Handles the HTTP POST request. - # Attempts to interpret all HTTP POST requests as XML-RPC calls, - # which are forwarded to the server's _dispatch method for handling. - - # Note: this method is the same as in SimpleXMLRPCRequestHandler, - # just hacked to handle cookies - - # Check that the path is legal - if not self.is_rpc_path_valid(): - self.report_404() - return - - try: - # Get arguments by reading body of request. - # We read this in chunks to avoid straining - # socket.read(); around the 10 or 15Mb mark, some platforms - # begin to have problems (bug #792570). - max_chunk_size = 10 * 1024 * 1024 - size_remaining = int(self.headers["content-length"]) - L = [] - while size_remaining: - chunk_size = min(size_remaining, max_chunk_size) - L.append(self.rfile.read(chunk_size)) - size_remaining -= len(L[-1]) - data = ''.join(L) - - # In previous versions of SimpleXMLRPCServer, _dispatch - # could be overridden in this class, instead of in - # SimpleXMLRPCDispatcher. To maintain backwards compatibility, - # check to see if a subclass implements _dispatch and dispatch - # using that method if present. - response = self.server._marshaled_dispatch( - data, getattr(self, '_dispatch', None) - ) - except: # This should only happen if the module is buggy - # internal error, report as HTTP server error - self.send_response(500) - self.end_headers() - else: - # got a valid XML RPC response - self.send_response(200) - self.send_header("Content-type", "text/xml") - self.send_header("Content-length", str(len(response))) - - # HACK :start -> sends cookies here - if self.cookies: - for cookie in self.cookies: - self.send_header('Set-Cookie', cookie.output(header='')) - # HACK :end - - self.end_headers() - self.wfile.write(response) - - # shut down the connection - self.wfile.flush() - self.connection.shutdown(1) - - def APIAuthenticateClient(self): - if 'Authorization' in self.headers: - # handle Basic authentication - (enctype, encstr) = self.headers.get('Authorization').split() - (emailid, password) = encstr.decode('base64').split(':') - if emailid == shared.config.get('bitmessagesettings', 'apiusername') and password == shared.config.get('bitmessagesettings', 'apipassword'): - return True - else: - return False - else: - logger.warn('Authentication failed because header lacks Authentication field') - time.sleep(2) - return False - - return False - - def _decode(self, text, decode_type): - try: - return text.decode(decode_type) - except Exception as e: - raise APIError(22, "Decode error - " + str(e) + ". Had trouble while decoding string: " + repr(text)) - - def _verifyAddress(self, address): - status, addressVersionNumber, streamNumber, ripe = decodeAddress(address) - if status != 'success': - logger.warn('API Error 0007: Could not decode address %s. Status: %s.', address, status) - - if status == 'checksumfailed': - raise APIError(8, 'Checksum failed for address: ' + address) - if status == 'invalidcharacters': - raise APIError(9, 'Invalid characters in address: ' + address) - if status == 'versiontoohigh': - raise APIError(10, 'Address version number too high (or zero) in address: ' + address) - raise APIError(7, 'Could not decode address: ' + address + ' : ' + status) - if addressVersionNumber < 2 or addressVersionNumber > 4: - raise APIError(11, 'The address version number currently must be 2, 3 or 4. Others aren\'t supported. Check the address.') - if streamNumber != 1: - raise APIError(12, 'The stream number must be 1. Others aren\'t supported. Check the address.') - - return (status, addressVersionNumber, streamNumber, ripe) - - def _handle_request(self, method, params): - if method == 'helloWorld': - (a, b) = params - return a + '-' + b - elif method == 'add': - (a, b) = params - return a + b - elif method == 'statusBar': - message, = params - shared.UISignalQueue.put(('updateStatusBar', message)) - elif method == 'listAddresses' or method == 'listAddresses2': - data = '{"addresses":[' - configSections = shared.config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile != 'bitmessagesettings': - status, addressVersionNumber, streamNumber, hash = decodeAddress( - addressInKeysFile) - data - if len(data) > 20: - data += ',' - if shared.config.has_option(addressInKeysFile, 'chan'): - chan = shared.config.getboolean(addressInKeysFile, 'chan') - else: - chan = False - label = shared.config.get(addressInKeysFile, 'label') - if method == 'listAddresses2': - label = label.encode('base64') - data += json.dumps({'label': label, 'address': addressInKeysFile, 'stream': - streamNumber, 'enabled': shared.config.getboolean(addressInKeysFile, 'enabled'), 'chan': chan}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'listAddressBookEntries' or method == 'listAddressbook': # the listAddressbook alias should be removed eventually. - queryreturn = sqlQuery('''SELECT label, address from addressbook''') - data = '{"addresses":[' - for row in queryreturn: - label, address = row - label = shared.fixPotentiallyInvalidUTF8Data(label) - if len(data) > 20: - data += ',' - data += json.dumps({'label':label.encode('base64'), 'address': address}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'addAddressBookEntry' or method == 'addAddressbook': # the addAddressbook alias should be deleted eventually. - if len(params) != 2: - raise APIError(0, "I need label and address") - address, label = params - label = self._decode(label, "base64") - address = addBMIfNotPresent(address) - self._verifyAddress(address) - queryreturn = sqlQuery("SELECT address FROM addressbook WHERE address=?", address) - if queryreturn != []: - raise APIError(16, 'You already have this address in your address book.') - - sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address) - shared.UISignalQueue.put(('rerenderInboxFromLabels','')) - shared.UISignalQueue.put(('rerenderSentToLabels','')) - shared.UISignalQueue.put(('rerenderAddressBook','')) - return "Added address %s to address book" % address - elif method == 'deleteAddressBookEntry' or method == 'deleteAddressbook': # The deleteAddressbook alias should be deleted eventually. - if len(params) != 1: - raise APIError(0, "I need an address") - address, = params - address = addBMIfNotPresent(address) - self._verifyAddress(address) - sqlExecute('DELETE FROM addressbook WHERE address=?', address) - shared.UISignalQueue.put(('rerenderInboxFromLabels','')) - shared.UISignalQueue.put(('rerenderSentToLabels','')) - shared.UISignalQueue.put(('rerenderAddressBook','')) - return "Deleted address book entry for %s if it existed" % address - elif method == 'createRandomAddress': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - elif len(params) == 1: - label, = params - eighteenByteRipe = False - nonceTrialsPerByte = shared.config.get( - 'bitmessagesettings', 'defaultnoncetrialsperbyte') - payloadLengthExtraBytes = shared.config.get( - 'bitmessagesettings', 'defaultpayloadlengthextrabytes') - elif len(params) == 2: - label, eighteenByteRipe = params - nonceTrialsPerByte = shared.config.get( - 'bitmessagesettings', 'defaultnoncetrialsperbyte') - payloadLengthExtraBytes = shared.config.get( - 'bitmessagesettings', 'defaultpayloadlengthextrabytes') - elif len(params) == 3: - label, eighteenByteRipe, totalDifficulty = params - nonceTrialsPerByte = int( - shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) - payloadLengthExtraBytes = shared.config.get( - 'bitmessagesettings', 'defaultpayloadlengthextrabytes') - elif len(params) == 4: - label, eighteenByteRipe, totalDifficulty, smallMessageDifficulty = params - nonceTrialsPerByte = int( - shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) - payloadLengthExtraBytes = int( - shared.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) - else: - raise APIError(0, 'Too many parameters!') - label = self._decode(label, "base64") - try: - unicode(label, 'utf-8') - except: - raise APIError(17, 'Label is not valid UTF-8 data.') - shared.apiAddressGeneratorReturnQueue.queue.clear() - streamNumberForAddress = 1 - shared.addressGeneratorQueue.put(( - 'createRandomAddress', 4, streamNumberForAddress, label, 1, "", eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes)) - return shared.apiAddressGeneratorReturnQueue.get() - elif method == 'createDeterministicAddresses': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - elif len(params) == 1: - passphrase, = params - numberOfAddresses = 1 - addressVersionNumber = 0 - streamNumber = 0 - eighteenByteRipe = False - nonceTrialsPerByte = shared.config.get( - 'bitmessagesettings', 'defaultnoncetrialsperbyte') - payloadLengthExtraBytes = shared.config.get( - 'bitmessagesettings', 'defaultpayloadlengthextrabytes') - elif len(params) == 2: - passphrase, numberOfAddresses = params - addressVersionNumber = 0 - streamNumber = 0 - eighteenByteRipe = False - nonceTrialsPerByte = shared.config.get( - 'bitmessagesettings', 'defaultnoncetrialsperbyte') - payloadLengthExtraBytes = shared.config.get( - 'bitmessagesettings', 'defaultpayloadlengthextrabytes') - elif len(params) == 3: - passphrase, numberOfAddresses, addressVersionNumber = params - streamNumber = 0 - eighteenByteRipe = False - nonceTrialsPerByte = shared.config.get( - 'bitmessagesettings', 'defaultnoncetrialsperbyte') - payloadLengthExtraBytes = shared.config.get( - 'bitmessagesettings', 'defaultpayloadlengthextrabytes') - elif len(params) == 4: - passphrase, numberOfAddresses, addressVersionNumber, streamNumber = params - eighteenByteRipe = False - nonceTrialsPerByte = shared.config.get( - 'bitmessagesettings', 'defaultnoncetrialsperbyte') - payloadLengthExtraBytes = shared.config.get( - 'bitmessagesettings', 'defaultpayloadlengthextrabytes') - elif len(params) == 5: - passphrase, numberOfAddresses, addressVersionNumber, streamNumber, eighteenByteRipe = params - nonceTrialsPerByte = shared.config.get( - 'bitmessagesettings', 'defaultnoncetrialsperbyte') - payloadLengthExtraBytes = shared.config.get( - 'bitmessagesettings', 'defaultpayloadlengthextrabytes') - elif len(params) == 6: - passphrase, numberOfAddresses, addressVersionNumber, streamNumber, eighteenByteRipe, totalDifficulty = params - nonceTrialsPerByte = int( - shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) - payloadLengthExtraBytes = shared.config.get( - 'bitmessagesettings', 'defaultpayloadlengthextrabytes') - elif len(params) == 7: - passphrase, numberOfAddresses, addressVersionNumber, streamNumber, eighteenByteRipe, totalDifficulty, smallMessageDifficulty = params - nonceTrialsPerByte = int( - shared.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) - payloadLengthExtraBytes = int( - shared.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) - else: - raise APIError(0, 'Too many parameters!') - if len(passphrase) == 0: - raise APIError(1, 'The specified passphrase is blank.') - if not isinstance(eighteenByteRipe, bool): - raise APIError(23, 'Bool expected in eighteenByteRipe, saw %s instead' % type(eighteenByteRipe)) - passphrase = self._decode(passphrase, "base64") - if addressVersionNumber == 0: # 0 means "just use the proper addressVersionNumber" - addressVersionNumber = 4 - if addressVersionNumber != 3 and addressVersionNumber != 4: - raise APIError(2,'The address version number currently must be 3, 4, or 0 (which means auto-select). ' + addressVersionNumber + ' isn\'t supported.') - if streamNumber == 0: # 0 means "just use the most available stream" - streamNumber = 1 - if streamNumber != 1: - raise APIError(3,'The stream number must be 1 (or 0 which means auto-select). Others aren\'t supported.') - if numberOfAddresses == 0: - raise APIError(4, 'Why would you ask me to generate 0 addresses for you?') - if numberOfAddresses > 999: - raise APIError(5, 'You have (accidentally?) specified too many addresses to make. Maximum 999. This check only exists to prevent mischief; if you really want to create more addresses than this, contact the Bitmessage developers and we can modify the check or you can do it yourself by searching the source code for this message.') - shared.apiAddressGeneratorReturnQueue.queue.clear() - logger.debug('Requesting that the addressGenerator create %s addresses.', numberOfAddresses) - shared.addressGeneratorQueue.put( - ('createDeterministicAddresses', addressVersionNumber, streamNumber, - 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes)) - data = '{"addresses":[' - queueReturn = shared.apiAddressGeneratorReturnQueue.get() - for item in queueReturn: - if len(data) > 20: - data += ',' - data += "\"" + item + "\"" - data += ']}' - return data - elif method == 'getDeterministicAddress': - if len(params) != 3: - raise APIError(0, 'I need exactly 3 parameters.') - passphrase, addressVersionNumber, streamNumber = params - numberOfAddresses = 1 - eighteenByteRipe = False - if len(passphrase) == 0: - raise APIError(1, 'The specified passphrase is blank.') - passphrase = self._decode(passphrase, "base64") - if addressVersionNumber != 3 and addressVersionNumber != 4: - raise APIError(2, 'The address version number currently must be 3 or 4. ' + addressVersionNumber + ' isn\'t supported.') - if streamNumber != 1: - raise APIError(3, ' The stream number must be 1. Others aren\'t supported.') - shared.apiAddressGeneratorReturnQueue.queue.clear() - logger.debug('Requesting that the addressGenerator create %s addresses.', numberOfAddresses) - shared.addressGeneratorQueue.put( - ('getDeterministicAddress', addressVersionNumber, - streamNumber, 'unused API address', numberOfAddresses, passphrase, eighteenByteRipe)) - return shared.apiAddressGeneratorReturnQueue.get() - - elif method == 'createChan': - if len(params) == 0: - raise APIError(0, 'I need parameters.') - elif len(params) == 1: - passphrase, = params - passphrase = self._decode(passphrase, "base64") - if len(passphrase) == 0: - raise APIError(1, 'The specified passphrase is blank.') - # It would be nice to make the label the passphrase but it is - # possible that the passphrase contains non-utf-8 characters. - try: - unicode(passphrase, 'utf-8') - label = str_chan + ' ' + passphrase - except: - label = str_chan + ' ' + repr(passphrase) - - addressVersionNumber = 4 - streamNumber = 1 - shared.apiAddressGeneratorReturnQueue.queue.clear() - logger.debug('Requesting that the addressGenerator create chan %s.', passphrase) - shared.addressGeneratorQueue.put(('createChan', addressVersionNumber, streamNumber, label, passphrase)) - queueReturn = shared.apiAddressGeneratorReturnQueue.get() - if len(queueReturn) == 0: - raise APIError(24, 'Chan address is already present.') - address = queueReturn[0] - return address - elif method == 'joinChan': - if len(params) < 2: - raise APIError(0, 'I need two parameters.') - elif len(params) == 2: - passphrase, suppliedAddress= params - passphrase = self._decode(passphrase, "base64") - if len(passphrase) == 0: - raise APIError(1, 'The specified passphrase is blank.') - # It would be nice to make the label the passphrase but it is - # possible that the passphrase contains non-utf-8 characters. - try: - unicode(passphrase, 'utf-8') - label = str_chan + ' ' + passphrase - except: - label = str_chan + ' ' + repr(passphrase) - - status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(suppliedAddress) - suppliedAddress = addBMIfNotPresent(suppliedAddress) - shared.apiAddressGeneratorReturnQueue.queue.clear() - shared.addressGeneratorQueue.put(('joinChan', suppliedAddress, label, passphrase)) - addressGeneratorReturnValue = shared.apiAddressGeneratorReturnQueue.get() - - if addressGeneratorReturnValue == 'chan name does not match address': - raise APIError(18, 'Chan name does not match address.') - if len(addressGeneratorReturnValue) == 0: - raise APIError(24, 'Chan address is already present.') - createdAddress = addressGeneratorReturnValue[0] # in case we ever want it for anything. - return "success" - elif method == 'leaveChan': - if len(params) == 0: - raise APIError(0, 'I need parameters.') - elif len(params) == 1: - address, = params - status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) - address = addBMIfNotPresent(address) - if not shared.config.has_section(address): - raise APIError(13, 'Could not find this address in your keys.dat file.') - if not shared.safeConfigGetBoolean(address, 'chan'): - raise APIError(25, 'Specified address is not a chan address. Use deleteAddress API call instead.') - shared.config.remove_section(address) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) - return 'success' - - elif method == 'deleteAddress': - if len(params) == 0: - raise APIError(0, 'I need parameters.') - elif len(params) == 1: - address, = params - status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) - address = addBMIfNotPresent(address) - if not shared.config.has_section(address): - raise APIError(13, 'Could not find this address in your keys.dat file.') - shared.config.remove_section(address) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) - shared.UISignalQueue.put(('rerenderInboxFromLabels','')) - shared.UISignalQueue.put(('rerenderSentToLabels','')) - shared.reloadMyAddressHashes() - return 'success' - - elif method == 'getAllInboxMessages': - queryreturn = sqlQuery( - '''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox where folder='inbox' ORDER BY received''') - data = '{"inboxMessages":[' - for row in queryreturn: - msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - message = shared.fixPotentiallyInvalidUTF8Data(message) - if len(data) > 25: - data += ',' - data += json.dumps({'msgid': msgid.encode('hex'), 'toAddress': toAddress, 'fromAddress': fromAddress, 'subject': subject.encode( - 'base64'), 'message': message.encode('base64'), 'encodingType': encodingtype, 'receivedTime': received, 'read': read}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'getAllInboxMessageIds' or method == 'getAllInboxMessageIDs': - queryreturn = sqlQuery( - '''SELECT msgid FROM inbox where folder='inbox' ORDER BY received''') - data = '{"inboxMessageIds":[' - for row in queryreturn: - msgid = row[0] - if len(data) > 25: - data += ',' - data += json.dumps({'msgid': msgid.encode('hex')}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'getInboxMessageById' or method == 'getInboxMessageByID': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - elif len(params) == 1: - msgid = self._decode(params[0], "hex") - elif len(params) >= 2: - msgid = self._decode(params[0], "hex") - readStatus = params[1] - if not isinstance(readStatus, bool): - raise APIError(23, 'Bool expected in readStatus, saw %s instead.' % type(readStatus)) - queryreturn = sqlQuery('''SELECT read FROM inbox WHERE msgid=?''', msgid) - # UPDATE is slow, only update if status is different - if queryreturn != [] and (queryreturn[0][0] == 1) != readStatus: - sqlExecute('''UPDATE inbox set read = ? WHERE msgid=?''', readStatus, msgid) - shared.UISignalQueue.put(('changedInboxUnread', None)) - queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype, read FROM inbox WHERE msgid=?''', msgid) - data = '{"inboxMessage":[' - for row in queryreturn: - msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - message = shared.fixPotentiallyInvalidUTF8Data(message) - data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received, 'read': read}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'getAllSentMessages': - queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent where folder='sent' ORDER BY lastactiontime''') - data = '{"sentMessages":[' - for row in queryreturn: - msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - message = shared.fixPotentiallyInvalidUTF8Data(message) - if len(data) > 25: - data += ',' - data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'getAllSentMessageIds' or method == 'getAllSentMessageIDs': - queryreturn = sqlQuery('''SELECT msgid FROM sent where folder='sent' ORDER BY lastactiontime''') - data = '{"sentMessageIds":[' - for row in queryreturn: - msgid = row[0] - if len(data) > 25: - data += ',' - data += json.dumps({'msgid':msgid.encode('hex')}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'getInboxMessagesByReceiver' or method == 'getInboxMessagesByAddress': #after some time getInboxMessagesByAddress should be removed - if len(params) == 0: - raise APIError(0, 'I need parameters!') - toAddress = params[0] - queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, received, message, encodingtype FROM inbox WHERE folder='inbox' AND toAddress=?''', toAddress) - data = '{"inboxMessages":[' - for row in queryreturn: - msgid, toAddress, fromAddress, subject, received, message, encodingtype = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - message = shared.fixPotentiallyInvalidUTF8Data(message) - if len(data) > 25: - data += ',' - data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'getSentMessageById' or method == 'getSentMessageByID': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - msgid = self._decode(params[0], "hex") - queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE msgid=?''', msgid) - data = '{"sentMessage":[' - for row in queryreturn: - msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - message = shared.fixPotentiallyInvalidUTF8Data(message) - data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'getSentMessagesByAddress' or method == 'getSentMessagesBySender': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - fromAddress = params[0] - queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE folder='sent' AND fromAddress=? ORDER BY lastactiontime''', - fromAddress) - data = '{"sentMessages":[' - for row in queryreturn: - msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - message = shared.fixPotentiallyInvalidUTF8Data(message) - if len(data) > 25: - data += ',' - data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'getSentMessageByAckData': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - ackData = self._decode(params[0], "hex") - queryreturn = sqlQuery('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent WHERE ackdata=?''', - ackData) - data = '{"sentMessage":[' - for row in queryreturn: - msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - message = shared.fixPotentiallyInvalidUTF8Data(message) - data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'trashMessage': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - msgid = self._decode(params[0], "hex") - - # Trash if in inbox table - helper_inbox.trash(msgid) - # Trash if in sent table - sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) - return 'Trashed message (assuming message existed).' - elif method == 'trashInboxMessage': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - msgid = self._decode(params[0], "hex") - helper_inbox.trash(msgid) - return 'Trashed inbox message (assuming message existed).' - elif method == 'trashSentMessage': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - msgid = self._decode(params[0], "hex") - sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) - return 'Trashed sent message (assuming message existed).' - elif method == 'trashSentMessageByAckData': - # This API method should only be used when msgid is not available - if len(params) == 0: - raise APIError(0, 'I need parameters!') - ackdata = self._decode(params[0], "hex") - sqlExecute('''UPDATE sent SET folder='trash' WHERE ackdata=?''', - ackdata) - return 'Trashed sent message (assuming message existed).' - elif method == 'sendMessage': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - elif len(params) == 4: - toAddress, fromAddress, subject, message = params - encodingType = 2 - elif len(params) == 5: - toAddress, fromAddress, subject, message, encodingType = params - if encodingType != 2: - raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') - subject = self._decode(subject, "base64") - message = self._decode(message, "base64") - toAddress = addBMIfNotPresent(toAddress) - fromAddress = addBMIfNotPresent(fromAddress) - status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(toAddress) - self._verifyAddress(fromAddress) - try: - fromAddressEnabled = shared.config.getboolean( - fromAddress, 'enabled') - except: - raise APIError(13, 'Could not find your fromAddress in the keys.dat file.') - if not fromAddressEnabled: - raise APIError(14, 'Your fromAddress is disabled. Cannot send.') - - ackdata = OpenSSL.rand(32) - - t = ('', toAddress, toRipe, fromAddress, subject, message, ackdata, int( - time.time()), 'msgqueued', 1, 1, 'sent', 2) - helper_sent.insert(t) - - toLabel = '' - queryreturn = sqlQuery('''select label from addressbook where address=?''', toAddress) - if queryreturn != []: - for row in queryreturn: - toLabel, = row - # apiSignalQueue.put(('displayNewSentMessage',(toAddress,toLabel,fromAddress,subject,message,ackdata))) - shared.UISignalQueue.put(('displayNewSentMessage', ( - toAddress, toLabel, fromAddress, subject, message, ackdata))) - - shared.workerQueue.put(('sendmessage', toAddress)) - - return ackdata.encode('hex') - - elif method == 'sendBroadcast': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - if len(params) == 3: - fromAddress, subject, message = params - encodingType = 2 - elif len(params) == 4: - fromAddress, subject, message, encodingType = params - if encodingType != 2: - raise APIError(6, 'The encoding type must be 2 because that is the only one this program currently supports.') - subject = self._decode(subject, "base64") - message = self._decode(message, "base64") - - fromAddress = addBMIfNotPresent(fromAddress) - self._verifyAddress(fromAddress) - try: - fromAddressEnabled = shared.config.getboolean( - fromAddress, 'enabled') - except: - raise APIError(13, 'could not find your fromAddress in the keys.dat file.') - ackdata = OpenSSL.rand(32) - toAddress = '[Broadcast subscribers]' - ripe = '' - - - t = ('', toAddress, ripe, fromAddress, subject, message, ackdata, int( - time.time()), 'broadcastqueued', 1, 1, 'sent', 2) - helper_sent.insert(t) - - toLabel = '[Broadcast subscribers]' - shared.UISignalQueue.put(('displayNewSentMessage', ( - toAddress, toLabel, fromAddress, subject, message, ackdata))) - shared.workerQueue.put(('sendbroadcast', '')) - - return ackdata.encode('hex') - elif method == 'getStatus': - if len(params) != 1: - raise APIError(0, 'I need one parameter!') - ackdata, = params - if len(ackdata) != 64: - raise APIError(15, 'The length of ackData should be 32 bytes (encoded in hex thus 64 characters).') - ackdata = self._decode(ackdata, "hex") - queryreturn = sqlQuery( - '''SELECT status FROM sent where ackdata=?''', - ackdata) - if queryreturn == []: - return 'notfound' - for row in queryreturn: - status, = row - return status - elif method == 'addSubscription': - if len(params) == 0: - raise APIError(0, 'I need parameters!') - if len(params) == 1: - address, = params - label = '' - if len(params) == 2: - address, label = params - label = self._decode(label, "base64") - try: - unicode(label, 'utf-8') - except: - raise APIError(17, 'Label is not valid UTF-8 data.') - if len(params) > 2: - raise APIError(0, 'I need either 1 or 2 parameters!') - address = addBMIfNotPresent(address) - self._verifyAddress(address) - # First we must check to see if the address is already in the - # subscriptions list. - queryreturn = sqlQuery('''select * from subscriptions where address=?''', address) - if queryreturn != []: - raise APIError(16, 'You are already subscribed to that address.') - sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',label, address, True) - shared.reloadBroadcastSendersForWhichImWatching() - shared.UISignalQueue.put(('rerenderInboxFromLabels', '')) - shared.UISignalQueue.put(('rerenderSubscriptions', '')) - return 'Added subscription.' - - elif method == 'deleteSubscription': - if len(params) != 1: - raise APIError(0, 'I need 1 parameter!') - address, = params - address = addBMIfNotPresent(address) - sqlExecute('''DELETE FROM subscriptions WHERE address=?''', address) - shared.reloadBroadcastSendersForWhichImWatching() - shared.UISignalQueue.put(('rerenderInboxFromLabels', '')) - shared.UISignalQueue.put(('rerenderSubscriptions', '')) - return 'Deleted subscription if it existed.' - elif method == 'listSubscriptions': - queryreturn = sqlQuery('''SELECT label, address, enabled FROM subscriptions''') - data = '{"subscriptions":[' - for row in queryreturn: - label, address, enabled = row - label = shared.fixPotentiallyInvalidUTF8Data(label) - if len(data) > 20: - data += ',' - data += json.dumps({'label':label.encode('base64'), 'address': address, 'enabled': enabled == 1}, indent=4, separators=(',',': ')) - data += ']}' - return data - elif method == 'disseminatePreEncryptedMsg': - # The device issuing this command to PyBitmessage supplies a msg object that has - # already been encrypted but which still needs the POW to be done. PyBitmessage - # accepts this msg object and sends it out to the rest of the Bitmessage network - # as if it had generated the message itself. Please do not yet add this to the - # api doc. - if len(params) != 3: - raise APIError(0, 'I need 3 parameter!') - encryptedPayload, requiredAverageProofOfWorkNonceTrialsPerByte, requiredPayloadLengthExtraBytes = params - encryptedPayload = self._decode(encryptedPayload, "hex") - # Let us do the POW and attach it to the front - target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte) - with shared.printLock: - print '(For msg message via API) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes - powStartTime = time.time() - initialHash = hashlib.sha512(encryptedPayload).digest() - trialValue, nonce = proofofwork.run(target, initialHash) - with shared.printLock: - print '(For msg message via API) Found proof of work', trialValue, 'Nonce:', nonce - try: - print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.' - except: - pass - encryptedPayload = pack('>Q', nonce) + encryptedPayload - toStreamNumber = decodeVarint(encryptedPayload[16:26])[0] - inventoryHash = calculateInventoryHash(encryptedPayload) - objectType = 'msg' - shared.inventory[inventoryHash] = ( - objectType, toStreamNumber, encryptedPayload, int(time.time()),'') - shared.inventorySets[toStreamNumber].add(inventoryHash) - with shared.printLock: - print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex') - shared.broadcastToSendDataQueues(( - toStreamNumber, 'advertiseobject', inventoryHash)) - elif method == 'disseminatePubkey': - # The device issuing this command to PyBitmessage supplies a pubkey object to be - # disseminated to the rest of the Bitmessage network. PyBitmessage accepts this - # pubkey object and sends it out to the rest of the Bitmessage network as if it - # had generated the pubkey object itself. Please do not yet add this to the api - # doc. - if len(params) != 1: - raise APIError(0, 'I need 1 parameter!') - payload, = params - payload = self._decode(payload, "hex") - - # Let us do the POW - target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes + - 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) - print '(For pubkey message via API) Doing proof of work...' - initialHash = hashlib.sha512(payload).digest() - trialValue, nonce = proofofwork.run(target, initialHash) - print '(For pubkey message via API) Found proof of work', trialValue, 'Nonce:', nonce - payload = pack('>Q', nonce) + payload - - pubkeyReadPosition = 8 # bypass the nonce - if payload[pubkeyReadPosition:pubkeyReadPosition+4] == '\x00\x00\x00\x00': # if this pubkey uses 8 byte time - pubkeyReadPosition += 8 - else: - pubkeyReadPosition += 4 - addressVersion, addressVersionLength = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10]) - pubkeyReadPosition += addressVersionLength - pubkeyStreamNumber = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10])[0] - inventoryHash = calculateInventoryHash(payload) - objectType = 'pubkey' - #todo: support v4 pubkeys - shared.inventory[inventoryHash] = ( - objectType, pubkeyStreamNumber, payload, int(time.time()),'') - shared.inventorySets[pubkeyStreamNumber].add(inventoryHash) - with shared.printLock: - print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex') - shared.broadcastToSendDataQueues(( - streamNumber, 'advertiseobject', inventoryHash)) - elif method == 'getMessageDataByDestinationHash' or method == 'getMessageDataByDestinationTag': - # Method will eventually be used by a particular Android app to - # select relevant messages. Do not yet add this to the api - # doc. - - if len(params) != 1: - raise APIError(0, 'I need 1 parameter!') - requestedHash, = params - if len(requestedHash) != 32: - raise APIError(19, 'The length of hash should be 32 bytes (encoded in hex thus 64 characters).') - requestedHash = self._decode(requestedHash, "hex") - - # This is not a particularly commonly used API function. Before we - # use it we'll need to fill out a field in our inventory database - # which is blank by default (first20bytesofencryptedmessage). - queryreturn = sqlQuery( - '''SELECT hash, payload FROM inventory WHERE tag = '' and objecttype = 'msg' ; ''') - with SqlBulkExecute() as sql: - for row in queryreturn: - hash, payload = row - readPosition = 16 # Nonce length + time length - readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length - t = (payload[readPosition:readPosition+32],hash) - sql.execute('''UPDATE inventory SET tag=? WHERE hash=?; ''', *t) - - queryreturn = sqlQuery('''SELECT payload FROM inventory WHERE tag = ?''', - requestedHash) - data = '{"receivedMessageDatas":[' - for row in queryreturn: - payload, = row - if len(data) > 25: - data += ',' - data += json.dumps({'data':payload.encode('hex')}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'getPubkeyByHash': - # Method will eventually be used by a particular Android app to - # retrieve pubkeys. Please do not yet add this to the api docs. - if len(params) != 1: - raise APIError(0, 'I need 1 parameter!') - requestedHash, = params - if len(requestedHash) != 40: - raise APIError(19, 'The length of hash should be 20 bytes (encoded in hex thus 40 characters).') - requestedHash = self._decode(requestedHash, "hex") - queryreturn = sqlQuery('''SELECT transmitdata FROM pubkeys WHERE hash = ? ; ''', requestedHash) - data = '{"pubkey":[' - for row in queryreturn: - transmitdata, = row - data += json.dumps({'data':transmitdata.encode('hex')}, indent=4, separators=(',', ': ')) - data += ']}' - return data - elif method == 'clientStatus': - if len(shared.connectedHostsList) == 0: - networkStatus = 'notConnected' - elif len(shared.connectedHostsList) > 0 and not shared.clientHasReceivedIncomingConnections: - networkStatus = 'connectedButHaveNotReceivedIncomingConnections' - else: - networkStatus = 'connectedAndReceivingIncomingConnections' - return json.dumps({'networkConnections':len(shared.connectedHostsList),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus, 'softwareName':'PyBitmessage','softwareVersion':shared.softwareVersion}, indent=4, separators=(',', ': ')) - elif method == 'decodeAddress': - # Return a meaningful decoding of an address. - if len(params) != 1: - raise APIError(0, 'I need 1 parameter!') - address, = params - status, addressVersion, streamNumber, ripe = decodeAddress(address) - return json.dumps({'status':status, 'addressVersion':addressVersion, - 'streamNumber':streamNumber, 'ripe':ripe.encode('base64')}, indent=4, - separators=(',', ': ')) - else: - raise APIError(20, 'Invalid method: %s' % method) - - def _dispatch(self, method, params): - self.cookies = [] - - validuser = self.APIAuthenticateClient() - if not validuser: - time.sleep(2) - return "RPC Username or password incorrect or HTTP header lacks authentication at all." - - try: - return self._handle_request(method, params) - except APIError as e: - return str(e) - except Exception as e: - logger.exception(e) - return "API Error 0021: Unexpected API Failure - %s" % str(e) - # This thread, of which there is only one, runs the API. - - class singleAPI(threading.Thread): def __init__(self): @@ -954,7 +87,6 @@ class singleAPI(threading.Thread): # This is a list of current connections (the thread pointers at least) selfInitiatedConnections = {} - if shared.useVeryEasyProofOfWorkForTesting: shared.networkDefaultProofOfWorkNonceTrialsPerByte = int( shared.networkDefaultProofOfWorkNonceTrialsPerByte / 16) @@ -986,6 +118,11 @@ class Main: sqlLookup.daemon = False # DON'T close the main program even if there are threads left. The closeEvent should command this thread to exit gracefully. sqlLookup.start() + # Start the thread that calculates POWs + objectProcessorThread = objectProcessor() + objectProcessorThread.daemon = False # DON'T close the main program even the thread remains. This thread checks the shutdown variable after processing each object. + objectProcessorThread.start() + # Start the cleanerThread singleCleanerThread = singleCleaner() singleCleanerThread.daemon = True # close the main program even if there are threads left @@ -1044,6 +181,7 @@ class Main: shared.doCleanShutdown() + #TODO: nice function but no one is using this def getApiAddress(self): if not shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): return None diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e601a4fc..7125cb60 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -16,6 +16,7 @@ import shared from bitmessageui import * from namecoin import namecoinConnection, ensureNamecoinOptions from newaddressdialog import * +from addaddressdialog import * from newsubscriptiondialog import * from regenerateaddresses import * from newchandialog import * @@ -2114,15 +2115,15 @@ class MyForm(QtGui.QMainWindow): self.ubuntuMessagingMenuUpdate(True, newItem, toLabel) def click_pushButtonAddAddressBook(self): - self.NewSubscriptionDialogInstance = NewSubscriptionDialog(self) - if self.NewSubscriptionDialogInstance.exec_(): - if self.NewSubscriptionDialogInstance.ui.labelSubscriptionAddressCheck.text() == _translate("MainWindow", "Address is valid."): + self.AddAddressDialogInstance = AddAddressDialog(self) + if self.AddAddressDialogInstance.exec_(): + if self.AddAddressDialogInstance.ui.labelAddressCheck.text() == _translate("MainWindow", "Address is valid."): # 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. address = addBMIfNotPresent(str( - self.NewSubscriptionDialogInstance.ui.lineEditSubscriptionAddress.text())) - label = self.NewSubscriptionDialogInstance.ui.newsubscriptionlabel.text().toUtf8() + self.AddAddressDialogInstance.ui.lineEditAddress.text())) + label = self.AddAddressDialogInstance.ui.newAddressLabel.text().toUtf8() self.addEntryToAddressBook(address,label) else: self.statusBar().showMessage(_translate( @@ -2171,7 +2172,7 @@ class MyForm(QtGui.QMainWindow): def click_pushButtonAddSubscription(self): self.NewSubscriptionDialogInstance = NewSubscriptionDialog(self) if self.NewSubscriptionDialogInstance.exec_(): - if self.NewSubscriptionDialogInstance.ui.labelSubscriptionAddressCheck.text() != _translate("MainWindow", "Address is valid."): + if self.NewSubscriptionDialogInstance.ui.labelAddressCheck.text() != _translate("MainWindow", "Address is valid."): self.statusBar().showMessage(_translate("MainWindow", "The address you entered was invalid. Ignoring it.")) return address = addBMIfNotPresent(str(self.NewSubscriptionDialogInstance.ui.lineEditSubscriptionAddress.text())) @@ -2181,6 +2182,22 @@ class MyForm(QtGui.QMainWindow): return label = self.NewSubscriptionDialogInstance.ui.newsubscriptionlabel.text().toUtf8() self.addSubscription(address, label) + # Now, if the user wants to display old broadcasts, let's get them out of the inventory and put them + # in the objectProcessorQueue to be processed + if self.NewSubscriptionDialogInstance.ui.checkBoxDisplayMessagesAlreadyInInventory.isChecked(): + status, addressVersion, streamNumber, ripe = decodeAddress(address) + shared.flushInventory() + doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( + addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest() + tag = doubleHashOfAddressData[32:] + queryreturn = sqlQuery( + '''select payload from inventory where objecttype='broadcast' and tag=?''', tag) + for row in queryreturn: + payload, = row + objectType = 'broadcast' + with shared.objectProcessorQueueSizeLock: + shared.objectProcessorQueueSize += len(payload) + shared.objectProcessorQueue.put((objectType,payload)) def loadBlackWhiteList(self): # Initialize the Blacklist or Whitelist table @@ -2419,11 +2436,11 @@ class MyForm(QtGui.QMainWindow): self.ui.tabWidget.setTabText(6, 'Whitelist') def click_pushButtonAddBlacklist(self): - self.NewBlacklistDialogInstance = NewSubscriptionDialog(self) + self.NewBlacklistDialogInstance = AddAddressDialog(self) if self.NewBlacklistDialogInstance.exec_(): - if self.NewBlacklistDialogInstance.ui.labelSubscriptionAddressCheck.text() == _translate("MainWindow", "Address is valid."): + if self.NewBlacklistDialogInstance.ui.labelAddressCheck.text() == _translate("MainWindow", "Address is valid."): address = addBMIfNotPresent(str( - self.NewBlacklistDialogInstance.ui.lineEditSubscriptionAddress.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. @@ -2437,7 +2454,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetBlacklist.setSortingEnabled(False) self.ui.tableWidgetBlacklist.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode( - self.NewBlacklistDialogInstance.ui.newsubscriptionlabel.text().toUtf8(), 'utf-8')) + self.NewBlacklistDialogInstance.ui.newAddressLabel.text().toUtf8(), 'utf-8')) newItem.setIcon(avatarize(address)) self.ui.tableWidgetBlacklist.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) @@ -2445,7 +2462,7 @@ class MyForm(QtGui.QMainWindow): QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableWidgetBlacklist.setItem(0, 1, newItem) self.ui.tableWidgetBlacklist.setSortingEnabled(True) - t = (str(self.NewBlacklistDialogInstance.ui.newsubscriptionlabel.text().toUtf8()), address, True) + t = (str(self.NewBlacklistDialogInstance.ui.newAddressLabel.text().toUtf8()), address, True) if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': sql = '''INSERT INTO blacklist VALUES (?,?,?)''' else: @@ -2630,8 +2647,14 @@ class MyForm(QtGui.QMainWindow): if shared.safeConfigGetBoolean(toAddressAtCurrentInboxRow, 'chan'): print 'original sent to a chan. Setting the to address in the reply to the chan address.' self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow)) - - self.ui.comboBoxSendFrom.setCurrentIndex(0) + + listOfAddressesInComboBoxSendFrom = [str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) for i in range(self.ui.comboBoxSendFrom.count())] + if toAddressAtCurrentInboxRow in listOfAddressesInComboBoxSendFrom: + currentIndex = listOfAddressesInComboBoxSendFrom.index(toAddressAtCurrentInboxRow) + self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) + else: + self.ui.comboBoxSendFrom.setCurrentIndex(0) + self.ui.textEditMessage.setText('\n\n------------------------------------------------------\n' + unicode(messageAtCurrentInboxRow, 'utf-8)')) if self.ui.tableWidgetInbox.item(currentInboxRow, 2).text()[0:3] in ['Re:', 'RE:']: self.ui.lineEditSubject.setText( @@ -3297,7 +3320,7 @@ class settingsDialog(QtGui.QDialog): shared.safeConfigGetBoolean('bitmessagesettings', 'useidenticons')) global languages - languages = ['system','en','eo','fr','de','es','ru','en_pirate','other'] + languages = ['system','en','eo','fr','de','es','ru','no','ar','zh_cn','en_pirate','other'] user_countrycode = str(shared.config.get('bitmessagesettings', 'userlocale')) if user_countrycode in languages: curr_index = languages.index(user_countrycode) @@ -3517,6 +3540,40 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog): QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) +class AddAddressDialog(QtGui.QDialog): + + def __init__(self, parent): + QtGui.QWidget.__init__(self, parent) + self.ui = Ui_AddAddressDialog() + self.ui.setupUi(self) + self.parent = parent + QtCore.QObject.connect(self.ui.lineEditAddress, QtCore.SIGNAL( + "textChanged(QString)"), self.addressChanged) + + def addressChanged(self, QString): + status, a, b, c = decodeAddress(str(QString)) + if status == 'missingbm': + self.ui.labelAddressCheck.setText(_translate( + "MainWindow", "The address should start with ''BM-''")) + elif status == 'checksumfailed': + self.ui.labelAddressCheck.setText(_translate( + "MainWindow", "The address is not typed or copied correctly (the checksum failed).")) + elif status == 'versiontoohigh': + self.ui.labelAddressCheck.setText(_translate( + "MainWindow", "The version number of this address is higher than this software can support. Please upgrade Bitmessage.")) + elif status == 'invalidcharacters': + self.ui.labelAddressCheck.setText(_translate( + "MainWindow", "The address contains invalid characters.")) + elif status == 'ripetooshort': + self.ui.labelAddressCheck.setText(_translate( + "MainWindow", "Some data encoded in the address is too short.")) + elif status == 'ripetoolong': + self.ui.labelAddressCheck.setText(_translate( + "MainWindow", "Some data encoded in the address is too long.")) + elif status == 'success': + self.ui.labelAddressCheck.setText( + _translate("MainWindow", "Address is valid.")) + class NewSubscriptionDialog(QtGui.QDialog): def __init__(self, parent): @@ -3525,31 +3582,56 @@ class NewSubscriptionDialog(QtGui.QDialog): self.ui.setupUi(self) self.parent = parent QtCore.QObject.connect(self.ui.lineEditSubscriptionAddress, QtCore.SIGNAL( - "textChanged(QString)"), self.subscriptionAddressChanged) + "textChanged(QString)"), self.addressChanged) + self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( + _translate("MainWindow", "Enter an address above.")) - def subscriptionAddressChanged(self, QString): - status, a, b, c = decodeAddress(str(QString)) + def addressChanged(self, QString): + self.ui.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(False) + self.ui.checkBoxDisplayMessagesAlreadyInInventory.setChecked(False) + status, addressVersion, streamNumber, ripe = decodeAddress(str(QString)) if status == 'missingbm': - self.ui.labelSubscriptionAddressCheck.setText(_translate( + self.ui.labelAddressCheck.setText(_translate( "MainWindow", "The address should start with ''BM-''")) elif status == 'checksumfailed': - self.ui.labelSubscriptionAddressCheck.setText(_translate( + self.ui.labelAddressCheck.setText(_translate( "MainWindow", "The address is not typed or copied correctly (the checksum failed).")) elif status == 'versiontoohigh': - self.ui.labelSubscriptionAddressCheck.setText(_translate( + self.ui.labelAddressCheck.setText(_translate( "MainWindow", "The version number of this address is higher than this software can support. Please upgrade Bitmessage.")) elif status == 'invalidcharacters': - self.ui.labelSubscriptionAddressCheck.setText(_translate( + self.ui.labelAddressCheck.setText(_translate( "MainWindow", "The address contains invalid characters.")) elif status == 'ripetooshort': - self.ui.labelSubscriptionAddressCheck.setText(_translate( + self.ui.labelAddressCheck.setText(_translate( "MainWindow", "Some data encoded in the address is too short.")) elif status == 'ripetoolong': - self.ui.labelSubscriptionAddressCheck.setText(_translate( + self.ui.labelAddressCheck.setText(_translate( "MainWindow", "Some data encoded in the address is too long.")) elif status == 'success': - self.ui.labelSubscriptionAddressCheck.setText( + self.ui.labelAddressCheck.setText( _translate("MainWindow", "Address is valid.")) + if addressVersion <= 3: + self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( + _translate("MainWindow", "Address is an old type. We cannot display its past broadcasts.")) + else: + shared.flushInventory() + doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( + addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest() + tag = doubleHashOfAddressData[32:] + queryreturn = sqlQuery( + '''select hash from inventory where objecttype='broadcast' and tag=?''', tag) + if len(queryreturn) == 0: + self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( + _translate("MainWindow", "There are no recent broadcasts from this address to display.")) + elif len(queryreturn) == 1: + self.ui.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(True) + self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( + _translate("MainWindow", "Display the %1 recent broadcast from this address.").arg(str(len(queryreturn)))) + else: + self.ui.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(True) + self.ui.checkBoxDisplayMessagesAlreadyInInventory.setText( + _translate("MainWindow", "Display the %1 recent broadcasts from this address.").arg(str(len(queryreturn)))) class NewAddressDialog(QtGui.QDialog): diff --git a/src/bitmessageqt/about.py b/src/bitmessageqt/about.py index 34f4c27b..d2687532 100644 --- a/src/bitmessageqt/about.py +++ b/src/bitmessageqt/about.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'about.ui' # -# Created: Wed Nov 06 23:01:43 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Tue Jan 21 22:29:38 2014 +# by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -66,7 +66,7 @@ class Ui_aboutDialog(object): aboutDialog.setWindowTitle(_translate("aboutDialog", "About", None)) self.label.setText(_translate("aboutDialog", "PyBitmessage", None)) self.labelVersion.setText(_translate("aboutDialog", "version ?", None)) - self.label_2.setText(_translate("aboutDialog", "

Copyright © 2012-2013 Jonathan Warren
Copyright © 2013 The Bitmessage Developers

", None)) + self.label_2.setText(_translate("aboutDialog", "

Copyright © 2012-2014 Jonathan Warren
Copyright © 2013-2014 The Bitmessage Developers

", None)) self.label_3.setText(_translate("aboutDialog", "

Distributed under the MIT/X11 software license; see http://www.opensource.org/licenses/mit-license.php

", None)) self.label_5.setText(_translate("aboutDialog", "This is Beta software.", None)) diff --git a/src/bitmessageqt/about.ui b/src/bitmessageqt/about.ui index 1bf89fd9..3deab41b 100644 --- a/src/bitmessageqt/about.ui +++ b/src/bitmessageqt/about.ui @@ -74,7 +74,7 @@ - <html><head/><body><p>Copyright © 2012-2013 Jonathan Warren<br/>Copyright © 2013 The Bitmessage Developers</p></body></html> + <html><head/><body><p>Copyright © 2012-2014 Jonathan Warren<br/>Copyright © 2013-2014 The Bitmessage Developers</p></body></html> Qt::AlignCenter diff --git a/src/bitmessageqt/addaddressdialog.py b/src/bitmessageqt/addaddressdialog.py new file mode 100644 index 00000000..5ed19e0a --- /dev/null +++ b/src/bitmessageqt/addaddressdialog.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'addaddressdialog.ui' +# +# Created: Sat Nov 30 20:35:38 2013 +# by: PyQt4 UI code generator 4.10.3 +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore, QtGui + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + def _fromUtf8(s): + return s + +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig) + +class Ui_AddAddressDialog(object): + def setupUi(self, AddAddressDialog): + AddAddressDialog.setObjectName(_fromUtf8("AddAddressDialog")) + AddAddressDialog.resize(368, 162) + self.formLayout = QtGui.QFormLayout(AddAddressDialog) + self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow) + self.formLayout.setObjectName(_fromUtf8("formLayout")) + self.label_2 = QtGui.QLabel(AddAddressDialog) + self.label_2.setObjectName(_fromUtf8("label_2")) + self.formLayout.setWidget(0, QtGui.QFormLayout.SpanningRole, self.label_2) + self.newAddressLabel = QtGui.QLineEdit(AddAddressDialog) + self.newAddressLabel.setObjectName(_fromUtf8("newAddressLabel")) + self.formLayout.setWidget(2, QtGui.QFormLayout.SpanningRole, self.newAddressLabel) + self.label = QtGui.QLabel(AddAddressDialog) + self.label.setObjectName(_fromUtf8("label")) + self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.label) + self.lineEditAddress = QtGui.QLineEdit(AddAddressDialog) + self.lineEditAddress.setObjectName(_fromUtf8("lineEditAddress")) + self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.lineEditAddress) + self.labelAddressCheck = QtGui.QLabel(AddAddressDialog) + self.labelAddressCheck.setText(_fromUtf8("")) + self.labelAddressCheck.setWordWrap(True) + self.labelAddressCheck.setObjectName(_fromUtf8("labelAddressCheck")) + self.formLayout.setWidget(6, QtGui.QFormLayout.SpanningRole, self.labelAddressCheck) + self.buttonBox = QtGui.QDialogButtonBox(AddAddressDialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName(_fromUtf8("buttonBox")) + self.formLayout.setWidget(7, QtGui.QFormLayout.FieldRole, self.buttonBox) + + self.retranslateUi(AddAddressDialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), AddAddressDialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), AddAddressDialog.reject) + QtCore.QMetaObject.connectSlotsByName(AddAddressDialog) + + def retranslateUi(self, AddAddressDialog): + AddAddressDialog.setWindowTitle(_translate("AddAddressDialog", "Add new entry", None)) + self.label_2.setText(_translate("AddAddressDialog", "Label", None)) + self.label.setText(_translate("AddAddressDialog", "Address", None)) + diff --git a/src/bitmessageqt/addaddressdialog.ui b/src/bitmessageqt/addaddressdialog.ui new file mode 100644 index 00000000..d7963e0a --- /dev/null +++ b/src/bitmessageqt/addaddressdialog.ui @@ -0,0 +1,97 @@ + + + AddAddressDialog + + + + 0 + 0 + 368 + 162 + + + + Add new entry + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Label + + + + + + + + + + Address + + + + + + + + + + + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AddAddressDialog + accept() + + + 360 + 234 + + + 157 + 256 + + + + + buttonBox + rejected() + AddAddressDialog + reject() + + + 360 + 240 + + + 286 + 256 + + + + + diff --git a/src/bitmessageqt/newsubscriptiondialog.py b/src/bitmessageqt/newsubscriptiondialog.py index b849d50f..d71eec0c 100644 --- a/src/bitmessageqt/newsubscriptiondialog.py +++ b/src/bitmessageqt/newsubscriptiondialog.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'newsubscriptiondialog.ui' # -# Created: Fri Oct 19 15:30:41 2012 -# by: PyQt4 UI code generator 4.9.4 +# Created: Sat Nov 30 21:53:38 2013 +# by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -12,41 +12,49 @@ from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: - _fromUtf8 = lambda s: s + def _fromUtf8(s): + return s + +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig) class Ui_NewSubscriptionDialog(object): def setupUi(self, NewSubscriptionDialog): NewSubscriptionDialog.setObjectName(_fromUtf8("NewSubscriptionDialog")) - NewSubscriptionDialog.resize(368, 192) + NewSubscriptionDialog.resize(368, 173) self.formLayout = QtGui.QFormLayout(NewSubscriptionDialog) - self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow) self.formLayout.setObjectName(_fromUtf8("formLayout")) self.label_2 = QtGui.QLabel(NewSubscriptionDialog) self.label_2.setObjectName(_fromUtf8("label_2")) - self.formLayout.setWidget(0, QtGui.QFormLayout.SpanningRole, self.label_2) + self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_2) self.newsubscriptionlabel = QtGui.QLineEdit(NewSubscriptionDialog) self.newsubscriptionlabel.setObjectName(_fromUtf8("newsubscriptionlabel")) - self.formLayout.setWidget(2, QtGui.QFormLayout.SpanningRole, self.newsubscriptionlabel) + self.formLayout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.newsubscriptionlabel) self.label = QtGui.QLabel(NewSubscriptionDialog) self.label.setObjectName(_fromUtf8("label")) - self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.label) - spacerItem = QtGui.QSpacerItem(302, 10, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.formLayout.setItem(4, QtGui.QFormLayout.FieldRole, spacerItem) + self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label) self.lineEditSubscriptionAddress = QtGui.QLineEdit(NewSubscriptionDialog) self.lineEditSubscriptionAddress.setObjectName(_fromUtf8("lineEditSubscriptionAddress")) - self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.lineEditSubscriptionAddress) + self.formLayout.setWidget(3, QtGui.QFormLayout.SpanningRole, self.lineEditSubscriptionAddress) + self.labelAddressCheck = QtGui.QLabel(NewSubscriptionDialog) + self.labelAddressCheck.setText(_fromUtf8("")) + self.labelAddressCheck.setWordWrap(True) + self.labelAddressCheck.setObjectName(_fromUtf8("labelAddressCheck")) + self.formLayout.setWidget(4, QtGui.QFormLayout.SpanningRole, self.labelAddressCheck) + self.checkBoxDisplayMessagesAlreadyInInventory = QtGui.QCheckBox(NewSubscriptionDialog) + self.checkBoxDisplayMessagesAlreadyInInventory.setEnabled(False) + self.checkBoxDisplayMessagesAlreadyInInventory.setObjectName(_fromUtf8("checkBoxDisplayMessagesAlreadyInInventory")) + self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.checkBoxDisplayMessagesAlreadyInInventory) self.buttonBox = QtGui.QDialogButtonBox(NewSubscriptionDialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName(_fromUtf8("buttonBox")) - self.formLayout.setWidget(8, QtGui.QFormLayout.FieldRole, self.buttonBox) - spacerItem1 = QtGui.QSpacerItem(20, 10, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.formLayout.setItem(7, QtGui.QFormLayout.FieldRole, spacerItem1) - self.labelSubscriptionAddressCheck = QtGui.QLabel(NewSubscriptionDialog) - self.labelSubscriptionAddressCheck.setText(_fromUtf8("")) - self.labelSubscriptionAddressCheck.setWordWrap(True) - self.labelSubscriptionAddressCheck.setObjectName(_fromUtf8("labelSubscriptionAddressCheck")) - self.formLayout.setWidget(6, QtGui.QFormLayout.SpanningRole, self.labelSubscriptionAddressCheck) + self.formLayout.setWidget(6, QtGui.QFormLayout.FieldRole, self.buttonBox) self.retranslateUi(NewSubscriptionDialog) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), NewSubscriptionDialog.accept) @@ -54,7 +62,8 @@ class Ui_NewSubscriptionDialog(object): QtCore.QMetaObject.connectSlotsByName(NewSubscriptionDialog) def retranslateUi(self, NewSubscriptionDialog): - NewSubscriptionDialog.setWindowTitle(QtGui.QApplication.translate("NewSubscriptionDialog", "Add new entry", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("NewSubscriptionDialog", "Label", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("NewSubscriptionDialog", "Address", None, QtGui.QApplication.UnicodeUTF8)) + NewSubscriptionDialog.setWindowTitle(_translate("NewSubscriptionDialog", "Add new entry", None)) + self.label_2.setText(_translate("NewSubscriptionDialog", "Label", None)) + self.label.setText(_translate("NewSubscriptionDialog", "Address", None)) + self.checkBoxDisplayMessagesAlreadyInInventory.setText(_translate("NewSubscriptionDialog", "CheckBox", None)) diff --git a/src/bitmessageqt/newsubscriptiondialog.ui b/src/bitmessageqt/newsubscriptiondialog.ui index 84475e09..ed8615f4 100644 --- a/src/bitmessageqt/newsubscriptiondialog.ui +++ b/src/bitmessageqt/newsubscriptiondialog.ui @@ -7,50 +7,54 @@ 0 0 368 - 192 + 173 Add new entry - - QFormLayout::AllNonFixedFieldsGrow - - + Label - + - + Address - - - - Qt::Vertical - - - - 302 - 10 - - - - - + - + + + + + + + true + + + + + + + false + + + CheckBox + + + + Qt::Horizontal @@ -60,29 +64,6 @@ - - - - Qt::Vertical - - - - 20 - 10 - - - - - - - - - - - true - - - diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 9b045e38..293c133b 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Tue Nov 05 22:56:35 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Mon Jan 20 14:26:31 2014 +# by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -88,6 +88,9 @@ class Ui_settingsDialog(object): self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8("")) + self.languageComboBox.addItem(_fromUtf8("")) + self.languageComboBox.addItem(_fromUtf8("")) + self.languageComboBox.addItem(_fromUtf8("")) self.formLayout_2.setWidget(0, QtGui.QFormLayout.LabelRole, self.languageComboBox) self.formLayout.setWidget(8, QtGui.QFormLayout.FieldRole, self.groupBox) self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8("")) @@ -397,8 +400,11 @@ class Ui_settingsDialog(object): self.languageComboBox.setItemText(4, _translate("settingsDialog", "Deutsch", "de")) self.languageComboBox.setItemText(5, _translate("settingsDialog", "Españl", "es")) self.languageComboBox.setItemText(6, _translate("settingsDialog", "русский язык", "ru")) - self.languageComboBox.setItemText(7, _translate("settingsDialog", "Pirate English", "en_pirate")) - self.languageComboBox.setItemText(8, _translate("settingsDialog", "Other (set in keys.dat)", "other")) + self.languageComboBox.setItemText(7, _translate("settingsDialog", "Norsk", "no")) + self.languageComboBox.setItemText(8, _translate("settingsDialog", "العربية", "ar")) + self.languageComboBox.setItemText(9, _translate("settingsDialog", "简体中文", "zh_cn")) + self.languageComboBox.setItemText(10, _translate("settingsDialog", "Pirate English", "en_pirate")) + self.languageComboBox.setItemText(11, _translate("settingsDialog", "Other (set in keys.dat)", "other")) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None)) self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None)) self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None)) diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index d2628f8f..2af19613 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -154,6 +154,21 @@ русский язык + + + Norsk + + + + + العربية + + + + + 简体中文 + + Pirate English diff --git a/src/build_osx.py b/src/build_osx.py index 96d46250..f2bf8378 100644 --- a/src/build_osx.py +++ b/src/build_osx.py @@ -1,7 +1,7 @@ from setuptools import setup name = "Bitmessage" -version = "0.4.1" +version = "0.4.2" mainscript = ["bitmessagemain.py"] setup( diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py new file mode 100644 index 00000000..20ef31ff --- /dev/null +++ b/src/class_objectProcessor.py @@ -0,0 +1,1213 @@ +import time +import threading +import shared +import hashlib +import random +from struct import unpack, pack +import sys +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 * +import helper_generic +import helper_bitcoin +import helper_inbox +import helper_sent +from helper_sql import * +import tr +from debug import logger + + +class objectProcessor(threading.Thread): + """ + The objectProcessor thread, of which there is only one, receives network + objecs (msg, broadcast, pubkey, getpubkey) from the receiveDataThreads. + """ + def __init__(self): + threading.Thread.__init__(self) + """ + It may be the case that the last time Bitmessage was running, the user + closed it before it finished processing everything in the + objectProcessorQueue. Assuming that Bitmessage wasn't closed forcefully, + it should have saved the data in the queue into the objectprocessorqueue + table. Let's pull it out. + """ + queryreturn = sqlQuery( + '''SELECT objecttype, data FROM objectprocessorqueue''') + with shared.objectProcessorQueueSizeLock: + for row in queryreturn: + objectType, data = row + shared.objectProcessorQueueSize += len(data) + shared.objectProcessorQueue.put((objectType,data)) + sqlExecute('''DELETE FROM objectprocessorqueue''') + logger.debug('Loaded %s objects from disk into the objectProcessorQueue.' % str(len(queryreturn))) + + + def run(self): + while True: + objectType, data = shared.objectProcessorQueue.get() + + if objectType == 'getpubkey': + self.processgetpubkey(data) + elif objectType == 'pubkey': + self.processpubkey(data) + elif objectType == 'msg': + self.processmsg(data) + elif objectType == 'broadcast': + self.processbroadcast(data) + elif objectType == 'checkShutdownVariable': # is more of a command, not an object type. Is used to get this thread past the queue.get() so that it will check the shutdown variable. + pass + else: + logger.critical('Error! Bug! The class_objectProcessor was passed an object type it doesn\'t recognize: %s' % str(objectType)) + + with shared.objectProcessorQueueSizeLock: + shared.objectProcessorQueueSize -= len(data) # We maintain objectProcessorQueueSize so that we will slow down requesting objects if too much data accumulates in the queue. + + if shared.shutdown: + time.sleep(.5) # Wait just a moment for most of the connections to close + numberOfObjectsThatWereInTheObjectProcessorQueue = 0 + with SqlBulkExecute() as sql: + while shared.objectProcessorQueueSize > 1: + objectType, data = shared.objectProcessorQueue.get() + sql.execute('''INSERT INTO objectprocessorqueue VALUES (?,?)''', + objectType,data) + with shared.objectProcessorQueueSizeLock: + shared.objectProcessorQueueSize -= len(data) # We maintain objectProcessorQueueSize so that we will slow down requesting objects if too much data accumulates in the queue. + numberOfObjectsThatWereInTheObjectProcessorQueue += 1 + logger.debug('Saved %s objects from the objectProcessorQueue to disk. objectProcessorThread exiting.' % str(numberOfObjectsThatWereInTheObjectProcessorQueue)) + shared.shutdown = 2 + break + + def processgetpubkey(self, data): + readPosition = 8 # bypass the nonce + embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) + + # This section is used for the transition from 32 bit time to 64 bit + # time in the protocol. + if embeddedTime == 0: + embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) + readPosition += 8 + else: + readPosition += 4 + + requestedAddressVersionNumber, addressVersionLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += addressVersionLength + streamNumber, streamNumberLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += streamNumberLength + + if requestedAddressVersionNumber == 0: + logger.debug('The requestedAddressVersionNumber of the pubkey request is zero. That doesn\'t make any sense. Ignoring it.') + return + elif requestedAddressVersionNumber == 1: + logger.debug('The requestedAddressVersionNumber of the pubkey request is 1 which isn\'t supported anymore. Ignoring it.') + return + elif requestedAddressVersionNumber > 4: + logger.debug('The requestedAddressVersionNumber of the pubkey request is too high. Can\'t understand. Ignoring it.') + return + + myAddress = '' + if requestedAddressVersionNumber <= 3 : + requestedHash = data[readPosition:readPosition + 20] + if len(requestedHash) != 20: + logger.debug('The length of the requested hash is not 20 bytes. Something is wrong. Ignoring.') + return + logger.info('the hash requested in this getpubkey request is: %s' % requestedHash.encode('hex')) + if requestedHash in shared.myAddressesByHash: # if this address hash is one of mine + myAddress = shared.myAddressesByHash[requestedHash] + elif requestedAddressVersionNumber >= 4: + requestedTag = data[readPosition:readPosition + 32] + if len(requestedTag) != 32: + logger.debug('The length of the requested tag is not 32 bytes. Something is wrong. Ignoring.') + return + logger.debug('the tag requested in this getpubkey request is: %s' % requestedTag.encode('hex')) + if requestedTag in shared.myAddressesByTag: + myAddress = shared.myAddressesByTag[requestedTag] + + if myAddress == '': + logger.info('This getpubkey request is not for any of my keys.') + return + + if decodeAddress(myAddress)[1] != requestedAddressVersionNumber: + logger.warning('(Within the processgetpubkey function) Someone requested one of my pubkeys but the requestedAddressVersionNumber doesn\'t match my actual address version number. Ignoring.') + return + if decodeAddress(myAddress)[2] != streamNumber: + logger.warning('(Within the processgetpubkey function) Someone requested one of my pubkeys but the stream number on which we heard this getpubkey object doesn\'t match this address\' stream number. Ignoring.') + return + if shared.safeConfigGetBoolean(myAddress, 'chan'): + logger.info('Ignoring getpubkey request because it is for one of my chan addresses. The other party should already have the pubkey.') + return + try: + lastPubkeySendTime = int(shared.config.get( + myAddress, 'lastpubkeysendtime')) + except: + lastPubkeySendTime = 0 + if lastPubkeySendTime > time.time() - shared.lengthOfTimeToHoldOnToAllPubkeys: # If the last time we sent our pubkey was more recent than 28 days ago... + logger.info('Found getpubkey-requested-item in my list of EC hashes BUT we already sent it recently. Ignoring request. The lastPubkeySendTime is: %s' % lastPubkeySendTime) + return + logger.info('Found getpubkey-requested-hash in my list of EC hashes. Telling Worker thread to do the POW for a pubkey message and send it out.') + if requestedAddressVersionNumber == 2: + shared.workerQueue.put(( + 'doPOWForMyV2Pubkey', requestedHash)) + elif requestedAddressVersionNumber == 3: + shared.workerQueue.put(( + 'sendOutOrStoreMyV3Pubkey', requestedHash)) + elif requestedAddressVersionNumber == 4: + shared.workerQueue.put(( + 'sendOutOrStoreMyV4Pubkey', myAddress)) + + def processpubkey(self, data): + pubkeyProcessingStartTime = time.time() + shared.numberOfPubkeysProcessed += 1 + shared.UISignalQueue.put(( + 'updateNumberOfPubkeysProcessed', 'no data')) + readPosition = 8 # bypass the nonce + embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) + + # This section is used for the transition from 32 bit time to 64 bit + # time in the protocol. + if embeddedTime == 0: + embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) + readPosition += 8 + else: + readPosition += 4 + + addressVersion, varintLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += varintLength + streamNumber, varintLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += varintLength + if addressVersion == 0: + logger.debug('(Within processpubkey) addressVersion of 0 doesn\'t make sense.') + return + if addressVersion > 4 or addressVersion == 1: + logger.info('This version of Bitmessage cannot handle version %s addresses.' % addressVersion) + return + if addressVersion == 2: + if len(data) < 146: # sanity check. This is the minimum possible length. + logger.debug('(within processpubkey) payloadLength less than 146. Sanity check failed.') + return + bitfieldBehaviors = data[readPosition:readPosition + 4] + readPosition += 4 + publicSigningKey = data[readPosition:readPosition + 64] + # Is it possible for a public key to be invalid such that trying to + # encrypt or sign with it will cause an error? If it is, we should + # probably test these keys here. + readPosition += 64 + publicEncryptionKey = data[readPosition:readPosition + 64] + if len(publicEncryptionKey) < 64: + logger.debug('publicEncryptionKey length less than 64. Sanity check failed.') + return + sha = hashlib.new('sha512') + sha.update( + '\x04' + publicSigningKey + '\x04' + publicEncryptionKey) + ripeHasher = hashlib.new('ripemd160') + ripeHasher.update(sha.digest()) + ripe = ripeHasher.digest() + + + logger.info('within recpubkey, addressVersion: %s, streamNumber: %s \n\ + ripe %s\n\ + publicSigningKey in hex: %s\n\ + publicEncryptionKey in hex: %s' % (addressVersion, + streamNumber, + ripe.encode('hex'), + publicSigningKey.encode('hex'), + publicEncryptionKey.encode('hex') + ) + ) + + queryreturn = sqlQuery( + '''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion) + if queryreturn != []: # if this pubkey is already in our database and if we have used it personally: + logger.info('We HAVE used this pubkey personally. Updating time.') + t = (ripe, addressVersion, data, embeddedTime, 'yes') + else: + logger.info('We have NOT used this pubkey personally. Inserting in database.') + t = (ripe, addressVersion, data, embeddedTime, 'no') + # This will also update the embeddedTime. + sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) + # shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) + self.possibleNewPubkey(ripe = ripe) + if addressVersion == 3: + if len(data) < 170: # sanity check. + logger.warning('(within processpubkey) payloadLength less than 170. Sanity check failed.') + return + bitfieldBehaviors = data[readPosition:readPosition + 4] + readPosition += 4 + publicSigningKey = '\x04' + data[readPosition:readPosition + 64] + # Is it possible for a public key to be invalid such that trying to + # encrypt or sign with it will cause an error? If it is, we should + # probably test these keys here. + readPosition += 64 + publicEncryptionKey = '\x04' + data[readPosition:readPosition + 64] + readPosition += 64 + specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += specifiedNonceTrialsPerByteLength + specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += specifiedPayloadLengthExtraBytesLength + endOfSignedDataPosition = readPosition + signatureLength, signatureLengthLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += signatureLengthLength + signature = data[readPosition:readPosition + signatureLength] + try: + if not highlevelcrypto.verify(data[8:endOfSignedDataPosition], signature, publicSigningKey.encode('hex')): + logger.warning('ECDSA verify failed (within processpubkey)') + return + logger.info('ECDSA verify passed (within processpubkey)') + except Exception as err: + logger.warning('ECDSA verify failed (within processpubkey) %s' % err) + return + + sha = hashlib.new('sha512') + sha.update(publicSigningKey + publicEncryptionKey) + ripeHasher = hashlib.new('ripemd160') + ripeHasher.update(sha.digest()) + ripe = ripeHasher.digest() + + + logger.info('within recpubkey, addressVersion: %s, streamNumber: %s \n\ + ripe %s\n\ + publicSigningKey in hex: %s\n\ + publicEncryptionKey in hex: %s' % (addressVersion, + streamNumber, + ripe.encode('hex'), + publicSigningKey.encode('hex'), + publicEncryptionKey.encode('hex') + ) + ) + + queryreturn = sqlQuery('''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion) + if queryreturn != []: # if this pubkey is already in our database and if we have used it personally: + logger.info('We HAVE used this pubkey personally. Updating time.') + t = (ripe, addressVersion, data, embeddedTime, 'yes') + else: + logger.info('We have NOT used this pubkey personally. Inserting in database.') + t = (ripe, addressVersion, data, embeddedTime, 'no') + # This will also update the embeddedTime. + sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) + self.possibleNewPubkey(ripe = ripe) + + if addressVersion == 4: + """ + There exist a function: shared.decryptAndCheckPubkeyPayload which does something almost + the same as this section of code. There are differences, however; one being that + decryptAndCheckPubkeyPayload requires that a cryptor object be created each time it is + run which is an expensive operation. This, on the other hand, keeps them saved in + the shared.neededPubkeys dictionary so that if an attacker sends us many + incorrectly-tagged pubkeys, which would force us to try to decrypt them, this code + would run and handle that event quite quickly. + """ + if len(data) < 350: # sanity check. + logger.debug('(within processpubkey) payloadLength less than 350. Sanity check failed.') + return + signedData = data[8:readPosition] # Some of the signed data is not encrypted so let's keep it for now. + tag = data[readPosition:readPosition + 32] + readPosition += 32 + encryptedData = data[readPosition:] + if tag not in shared.neededPubkeys: + logger.info('We don\'t need this v4 pubkey. We didn\'t ask for it.') + return + + # Let us try to decrypt the pubkey + cryptorObject = shared.neededPubkeys[tag] + try: + decryptedData = cryptorObject.decrypt(encryptedData) + except: + # Someone must have encrypted some data with a different key + # but tagged it with a tag for which we are watching. + logger.info('Pubkey decryption was unsuccessful.') + return + + readPosition = 0 + bitfieldBehaviors = decryptedData[readPosition:readPosition + 4] + readPosition += 4 + publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64] + # Is it possible for a public key to be invalid such that trying to + # encrypt or check a sig with it will cause an error? If it is, we + # should probably test these keys here. + readPosition += 64 + publicEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64] + readPosition += 64 + specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += specifiedNonceTrialsPerByteLength + specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += specifiedPayloadLengthExtraBytesLength + signedData += decryptedData[:readPosition] + signatureLength, signatureLengthLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += signatureLengthLength + signature = decryptedData[readPosition:readPosition + signatureLength] + try: + if not highlevelcrypto.verify(signedData, signature, publicSigningKey.encode('hex')): + logger.info('ECDSA verify failed (within processpubkey)') + return + logger.info('ECDSA verify passed (within processpubkey)') + except Exception as err: + logger.info('ECDSA verify failed (within processpubkey) %s' % err) + return + + sha = hashlib.new('sha512') + sha.update(publicSigningKey + publicEncryptionKey) + ripeHasher = hashlib.new('ripemd160') + ripeHasher.update(sha.digest()) + ripe = ripeHasher.digest() + + # We need to make sure that the tag on the outside of the encryption + # is the one generated from hashing these particular keys. + if tag != hashlib.sha512(hashlib.sha512(encodeVarint(addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest()[32:]: + logger.info('Someone was trying to act malicious: tag doesn\'t match the keys in this pubkey message. Ignoring it.') + return + + logger.info('within recpubkey, addressVersion: %s, streamNumber: %s \n\ + ripe %s\n\ + publicSigningKey in hex: %s\n\ + publicEncryptionKey in hex: %s' % (addressVersion, + streamNumber, + ripe.encode('hex'), + publicSigningKey.encode('hex'), + publicEncryptionKey.encode('hex') + ) + ) + + t = (ripe, addressVersion, signedData, embeddedTime, 'yes') + sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) + + fromAddress = encodeAddress(addressVersion, streamNumber, ripe) + # That this point we know that we have been waiting on this pubkey. + # This function will command the workerThread to start work on + # the messages that require it. + self.possibleNewPubkey(address = fromAddress) + + # Display timing data + timeRequiredToProcessPubkey = time.time( + ) - pubkeyProcessingStartTime + logger.debug('Time required to process this pubkey: %s' % timeRequiredToProcessPubkey) + + + def processmsg(self, data): + messageProcessingStartTime = time.time() + shared.numberOfMessagesProcessed += 1 + shared.UISignalQueue.put(( + 'updateNumberOfMessagesProcessed', 'no data')) + readPosition = 8 # bypass the nonce + embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) + + # This section is used for the transition from 32 bit time to 64 bit + # time in the protocol. + if embeddedTime == 0: + embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) + readPosition += 8 + else: + readPosition += 4 + streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = decodeVarint( + data[readPosition:readPosition + 9]) + readPosition += streamNumberAsClaimedByMsgLength + inventoryHash = calculateInventoryHash(data) + initialDecryptionSuccessful = False + # Let's check whether this is a message acknowledgement bound for us. + if data[readPosition:] in shared.ackdataForWhichImWatching: + logger.info('This msg IS an acknowledgement bound for me.') + del shared.ackdataForWhichImWatching[data[readPosition:]] + sqlExecute('UPDATE sent SET status=? WHERE ackdata=?', + 'ackreceived', data[readPosition:]) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[readPosition:], tr.translateText("MainWindow",'Acknowledgement of the message received. %1').arg(unicode( + time.strftime(shared.config.get('bitmessagesettings', 'timeformat'), time.localtime(int(time.time()))), 'utf-8'))))) + return + else: + logger.info('This was NOT an acknowledgement bound for me.') + + + # This is not an acknowledgement bound for me. See if it is a message + # bound for me by trying to decrypt it with my private keys. + for key, cryptorObject in shared.myECCryptorObjects.items(): + try: + decryptedData = cryptorObject.decrypt( + data[readPosition:]) + toRipe = key # This is the RIPE hash of my pubkeys. We need this below to compare to the destination_ripe included in the encrypted data. + initialDecryptionSuccessful = True + logger.info('EC decryption successful using key associated with ripe hash: %s' % key.encode('hex')) + break + except Exception as err: + pass + # print 'cryptorObject.decrypt Exception:', err + if not initialDecryptionSuccessful: + # This is not a message bound for me. + logger.info('Length of time program spent failing to decrypt this message: %s seconds.' % (time.time() - messageProcessingStartTime,)) + return + + # This is a message bound for me. + toAddress = shared.myAddressesByHash[ + toRipe] # Look up my address based on the RIPE hash. + readPosition = 0 + messageVersion, messageVersionLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += messageVersionLength + if messageVersion != 1: + logger.info('Cannot understand message versions other than one. Ignoring message.') + return + sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += sendersAddressVersionNumberLength + if sendersAddressVersionNumber == 0: + logger.info('Cannot understand sendersAddressVersionNumber = 0. Ignoring message.') + return + if sendersAddressVersionNumber > 4: + logger.info('Sender\'s address version number %s not yet supported. Ignoring message.' % sendersAddressVersionNumber) + return + if len(decryptedData) < 170: + logger.info('Length of the unencrypted data is unreasonably short. Sanity check failed. Ignoring message.') + return + sendersStreamNumber, sendersStreamNumberLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + if sendersStreamNumber == 0: + logger.info('sender\'s stream number is 0. Ignoring message.') + return + readPosition += sendersStreamNumberLength + behaviorBitfield = decryptedData[readPosition:readPosition + 4] + readPosition += 4 + pubSigningKey = '\x04' + decryptedData[ + readPosition:readPosition + 64] + readPosition += 64 + pubEncryptionKey = '\x04' + decryptedData[ + readPosition:readPosition + 64] + readPosition += 64 + if sendersAddressVersionNumber >= 3: + requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += varintLength + logger.info('sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s' % requiredAverageProofOfWorkNonceTrialsPerByte) + requiredPayloadLengthExtraBytes, varintLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += varintLength + logger.info('sender\'s requiredPayloadLengthExtraBytes is %s' % requiredPayloadLengthExtraBytes) + endOfThePublicKeyPosition = readPosition # needed for when we store the pubkey in our database of pubkeys for later use. + if toRipe != decryptedData[readPosition:readPosition + 20]: + logger.info('The original sender of this message did not send it to you. Someone is attempting a Surreptitious Forwarding Attack.\n\ + See: http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html \n\ + your toRipe: %s\n\ + embedded destination toRipe: %s' % (toRipe.encode('hex'), decryptedData[readPosition:readPosition + 20].encode('hex')) + ) + return + readPosition += 20 + messageEncodingType, messageEncodingTypeLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += messageEncodingTypeLength + messageLength, messageLengthLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += messageLengthLength + message = decryptedData[readPosition:readPosition + messageLength] + # print 'First 150 characters of message:', repr(message[:150]) + readPosition += messageLength + ackLength, ackLengthLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += ackLengthLength + ackData = decryptedData[readPosition:readPosition + ackLength] + readPosition += ackLength + positionOfBottomOfAckData = readPosition # needed to mark the end of what is covered by the signature + signatureLength, signatureLengthLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += signatureLengthLength + signature = decryptedData[ + readPosition:readPosition + signatureLength] + try: + if not highlevelcrypto.verify(decryptedData[:positionOfBottomOfAckData], signature, pubSigningKey.encode('hex')): + logger.debug('ECDSA verify failed') + return + logger.debug('ECDSA verify passed') + except Exception as err: + logger.debug('ECDSA verify failed %s' % err) + return + logger.debug('As a matter of intellectual curiosity, here is the Bitcoin address associated with the keys owned by the other person: %s ..and here is the testnet address: %s. The other person must take their private signing key from Bitmessage and import it into Bitcoin (or a service like Blockchain.info) for it to be of any use. Do not use this unless you know what you are doing.' % + (helper_bitcoin.calculateBitcoinAddressFromPubkey(pubSigningKey), helper_bitcoin.calculateTestnetAddressFromPubkey(pubSigningKey)) + ) + + # calculate the fromRipe. + sha = hashlib.new('sha512') + sha.update(pubSigningKey + pubEncryptionKey) + ripe = hashlib.new('ripemd160') + ripe.update(sha.digest()) + fromAddress = encodeAddress( + sendersAddressVersionNumber, sendersStreamNumber, ripe.digest()) + # Let's store the public key in case we want to reply to this + # person. + if sendersAddressVersionNumber <= 3: + sqlExecute( + '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', + ripe.digest(), + sendersAddressVersionNumber, + '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[messageVersionLength:endOfThePublicKeyPosition], + int(time.time()), + 'yes') + # This will check to see whether we happen to be awaiting this + # pubkey in order to send a message. If we are, it will do the POW + # and send it. + self.possibleNewPubkey(ripe=ripe.digest()) + elif sendersAddressVersionNumber >= 4: + sqlExecute( + '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', + ripe.digest(), + sendersAddressVersionNumber, + '\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[messageVersionLength:endOfThePublicKeyPosition], + int(time.time()), + 'yes') + # This will check to see whether we happen to be awaiting this + # pubkey in order to send a message. If we are, it will do the POW + # and send it. + self.possibleNewPubkey(address = fromAddress) + # If this message is bound for one of my version 3 addresses (or + # higher), then we must check to make sure it meets our demanded + # proof of work requirement. If this is bound for one of my chan + # addresses then we skip this check; the minimum network POW is + # fine. + if decodeAddress(toAddress)[1] >= 3 and not shared.safeConfigGetBoolean(toAddress, 'chan'): # If the toAddress version number is 3 or higher and not one of my chan addresses: + if not shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(fromAddress): # If I'm not friendly with this person: + requiredNonceTrialsPerByte = shared.config.getint( + toAddress, 'noncetrialsperbyte') + requiredPayloadLengthExtraBytes = shared.config.getint( + toAddress, 'payloadlengthextrabytes') + if not shared.isProofOfWorkSufficient(data, requiredNonceTrialsPerByte, requiredPayloadLengthExtraBytes): + print 'Proof of work in msg message insufficient only because it does not meet our higher requirement.' + return + blockMessage = False # Gets set to True if the user shouldn't see the message according to black or white lists. + if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': # If we are using a blacklist + queryreturn = sqlQuery( + '''SELECT label FROM blacklist where address=? and enabled='1' ''', + fromAddress) + if queryreturn != []: + logger.info('Message ignored because address is in blacklist.') + + blockMessage = True + else: # We're using a whitelist + queryreturn = sqlQuery( + '''SELECT label FROM whitelist where address=? and enabled='1' ''', + fromAddress) + if queryreturn == []: + logger.info('Message ignored because address not in whitelist.') + blockMessage = True + if not blockMessage: + toLabel = shared.config.get(toAddress, 'label') + if toLabel == '': + toLabel = toAddress + + if messageEncodingType == 2: + subject, body = self.decodeType2Message(message) + logger.info('Message subject (first 100 characters): %s' % repr(subject)[:100]) + elif messageEncodingType == 1: + body = message + subject = '' + elif messageEncodingType == 0: + logger.info('messageEncodingType == 0. Doing nothing with the message. They probably just sent it so that we would store their public key or send their ack data for them.') + else: + body = 'Unknown encoding type.\n\n' + repr(message) + subject = '' + if messageEncodingType != 0: + t = (inventoryHash, toAddress, fromAddress, subject, int( + time.time()), body, 'inbox', messageEncodingType, 0) + helper_inbox.insert(t) + + shared.UISignalQueue.put(('displayNewInboxMessage', ( + inventoryHash, toAddress, fromAddress, subject, body))) + + # If we are behaving as an API then we might need to run an + # outside command to let some program know that a new message + # has arrived. + if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + try: + apiNotifyPath = shared.config.get( + 'bitmessagesettings', 'apinotifypath') + except: + apiNotifyPath = '' + if apiNotifyPath != '': + call([apiNotifyPath, "newMessage"]) + + # Let us now check and see whether our receiving address is + # behaving as a mailing list + if shared.safeConfigGetBoolean(toAddress, 'mailinglist'): + try: + mailingListName = shared.config.get( + toAddress, 'mailinglistname') + except: + mailingListName = '' + # Let us send out this message as a broadcast + subject = self.addMailingListNameToSubject( + subject, mailingListName) + # Let us now send this message out as a broadcast + message = time.strftime("%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime( + )) + ' Message ostensibly from ' + fromAddress + ':\n\n' + body + fromAddress = toAddress # The fromAddress for the broadcast that we are about to send is the toAddress (my address) for the msg message we are currently processing. + ackdataForBroadcast = OpenSSL.rand( + 32) # We don't actually need the ackdataForBroadcast for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating. + toAddress = '[Broadcast subscribers]' + ripe = '' + + t = ('', toAddress, ripe, fromAddress, subject, message, ackdataForBroadcast, int( + time.time()), 'broadcastqueued', 1, 1, 'sent', 2) + helper_sent.insert(t) + + shared.UISignalQueue.put(('displayNewSentMessage', ( + toAddress, '[Broadcast subscribers]', fromAddress, subject, message, ackdataForBroadcast))) + shared.workerQueue.put(('sendbroadcast', '')) + + if self.ackDataHasAVaildHeader(ackData): + if ackData[4:16] == 'getpubkey\x00\x00\x00': + shared.checkAndSharegetpubkeyWithPeers(ackData[24:]) + elif ackData[4:16] == 'pubkey\x00\x00\x00\x00\x00\x00': + shared.checkAndSharePubkeyWithPeers(ackData[24:]) + elif ackData[4:16] == 'msg\x00\x00\x00\x00\x00\x00\x00\x00\x00': + shared.checkAndShareMsgWithPeers(ackData[24:]) + elif ackData[4:16] == 'broadcast\x00\x00\x00': + shared.checkAndShareBroadcastWithPeers(ackData[24:]) + + # Display timing data + timeRequiredToAttemptToDecryptMessage = time.time( + ) - messageProcessingStartTime + shared.successfullyDecryptMessageTimings.append( + timeRequiredToAttemptToDecryptMessage) + sum = 0 + for item in shared.successfullyDecryptMessageTimings: + sum += item + logger.debug('Time to decrypt this message successfully: %s\n\ + Average time for all message decryption successes since startup: %s.' % + (timeRequiredToAttemptToDecryptMessage, sum / len(shared.successfullyDecryptMessageTimings)) + ) + + + def processbroadcast(self, data): + messageProcessingStartTime = time.time() + shared.numberOfBroadcastsProcessed += 1 + shared.UISignalQueue.put(( + 'updateNumberOfBroadcastsProcessed', 'no data')) + inventoryHash = calculateInventoryHash(data) + readPosition = 8 # bypass the nonce + embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) + + # This section is used for the transition from 32 bit time to 64 bit + # time in the protocol. + if embeddedTime == 0: + embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) + readPosition += 8 + else: + readPosition += 4 + + broadcastVersion, broadcastVersionLength = decodeVarint( + data[readPosition:readPosition + 9]) + readPosition += broadcastVersionLength + if broadcastVersion < 1 or broadcastVersion > 3: + logger.debug('Cannot decode incoming broadcast versions higher than 3. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.') + return + if broadcastVersion == 1: + beginningOfPubkeyPosition = readPosition # used when we add the pubkey to our pubkey table + sendersAddressVersion, sendersAddressVersionLength = decodeVarint( + data[readPosition:readPosition + 9]) + if sendersAddressVersion <= 1 or sendersAddressVersion >= 3: + # Cannot decode senderAddressVersion higher than 2. Assuming + # the sender isn\'t being silly, you should upgrade Bitmessage + # because this message shall be ignored. + return + readPosition += sendersAddressVersionLength + if sendersAddressVersion == 2: + sendersStream, sendersStreamLength = decodeVarint( + data[readPosition:readPosition + 9]) + readPosition += sendersStreamLength + behaviorBitfield = data[readPosition:readPosition + 4] + readPosition += 4 + sendersPubSigningKey = '\x04' + \ + data[readPosition:readPosition + 64] + readPosition += 64 + sendersPubEncryptionKey = '\x04' + \ + data[readPosition:readPosition + 64] + readPosition += 64 + endOfPubkeyPosition = readPosition + sendersHash = data[readPosition:readPosition + 20] + if sendersHash not in shared.broadcastSendersForWhichImWatching: + # Display timing data + logger.debug('Time spent deciding that we are not interested in this v1 broadcast: %s' % (time.time() - messageProcessingStartTime,)) + return + # At this point, this message claims to be from sendersHash and + # we are interested in it. We still have to hash the public key + # to make sure it is truly the key that matches the hash, and + # also check the signiture. + readPosition += 20 + + sha = hashlib.new('sha512') + sha.update(sendersPubSigningKey + sendersPubEncryptionKey) + ripe = hashlib.new('ripemd160') + ripe.update(sha.digest()) + if ripe.digest() != sendersHash: + # The sender of this message lied. + return + messageEncodingType, messageEncodingTypeLength = decodeVarint( + data[readPosition:readPosition + 9]) + if messageEncodingType == 0: + return + readPosition += messageEncodingTypeLength + messageLength, messageLengthLength = decodeVarint( + data[readPosition:readPosition + 9]) + readPosition += messageLengthLength + message = data[readPosition:readPosition + messageLength] + readPosition += messageLength + readPositionAtBottomOfMessage = readPosition + signatureLength, signatureLengthLength = decodeVarint( + data[readPosition:readPosition + 9]) + readPosition += signatureLengthLength + signature = data[readPosition:readPosition + signatureLength] + try: + if not highlevelcrypto.verify(data[12:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')): + logger.debug('ECDSA verify failed') + return + logger.debug('ECDSA verify passed') + except Exception as err: + logger.debug('ECDSA verify failed %s' % err) + return + # verify passed + fromAddress = encodeAddress( + sendersAddressVersion, sendersStream, ripe.digest()) + logger.debug('fromAddress: %s' % fromAddress) + + # Let's store the public key in case we want to reply to this person. + # We don't have the correct nonce or time (which would let us + # send out a pubkey message) so we'll just fill it with 1's. We + # won't be able to send this pubkey to others (without doing + # the proof of work ourselves, which this program is programmed + # to not do.) + sqlExecute( + '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', + ripe.digest(), + sendersAddressVersion, + '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + data[beginningOfPubkeyPosition:endOfPubkeyPosition], + int(time.time()), + 'yes') + # This will check to see whether we happen to be awaiting this + # pubkey in order to send a message. If we are, it will do the + # POW and send it. + self.possibleNewPubkey(ripe=ripe.digest()) + + + if messageEncodingType == 2: + subject, body = decodeType2Message(message) + logger.info('Broadcast subject (first 100 characters): %s' % repr(subject)[:100]) + elif messageEncodingType == 1: + body = message + subject = '' + elif messageEncodingType == 0: + logger.debug('messageEncodingType == 0. Doing nothing with the message.') + else: + body = 'Unknown encoding type.\n\n' + repr(message) + subject = '' + + toAddress = '[Broadcast subscribers]' + if messageEncodingType != 0: + + t = (inventoryHash, toAddress, fromAddress, subject, int( + time.time()), body, 'inbox', messageEncodingType, 0) + helper_inbox.insert(t) + + shared.UISignalQueue.put(('displayNewInboxMessage', ( + inventoryHash, toAddress, fromAddress, subject, body))) + + # If we are behaving as an API then we might need to run an + # outside command to let some program know that a new + # message has arrived. + if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + try: + apiNotifyPath = shared.config.get( + 'bitmessagesettings', 'apinotifypath') + except: + apiNotifyPath = '' + if apiNotifyPath != '': + call([apiNotifyPath, "newBroadcast"]) + + # Display timing data + logger.debug('Time spent processing this interesting broadcast: %s' % (time.time() - messageProcessingStartTime,)) + + if broadcastVersion == 2: + cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += cleartextStreamNumberLength + initialDecryptionSuccessful = False + for key, cryptorObject in shared.MyECSubscriptionCryptorObjects.items(): + try: + decryptedData = cryptorObject.decrypt(data[readPosition:]) + toRipe = key # This is the RIPE hash of the sender's pubkey. We need this below to compare to the RIPE hash of the sender's address to verify that it was encrypted by with their key rather than some other key. + initialDecryptionSuccessful = True + logger.info('EC decryption successful using key associated with ripe hash: %s' % key.encode('hex')) + break + except Exception as err: + pass + # print 'cryptorObject.decrypt Exception:', err + if not initialDecryptionSuccessful: + # This is not a broadcast I am interested in. + logger.debug('Length of time program spent failing to decrypt this v2 broadcast: %s seconds.' % (time.time() - messageProcessingStartTime,)) + return + # At this point this is a broadcast I have decrypted and thus am + # interested in. + signedBroadcastVersion, readPosition = decodeVarint( + decryptedData[:10]) + beginningOfPubkeyPosition = readPosition # used when we add the pubkey to our pubkey table + sendersAddressVersion, sendersAddressVersionLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + if sendersAddressVersion < 2 or sendersAddressVersion > 3: + logger.info('Cannot decode senderAddressVersion other than 2 or 3. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.') + return + readPosition += sendersAddressVersionLength + sendersStream, sendersStreamLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + if sendersStream != cleartextStreamNumber: + logger.info('The stream number outside of the encryption on which the POW was completed doesn\'t match the stream number inside the encryption. Ignoring broadcast.') + return + readPosition += sendersStreamLength + behaviorBitfield = decryptedData[readPosition:readPosition + 4] + readPosition += 4 + sendersPubSigningKey = '\x04' + \ + decryptedData[readPosition:readPosition + 64] + readPosition += 64 + sendersPubEncryptionKey = '\x04' + \ + decryptedData[readPosition:readPosition + 64] + readPosition += 64 + if sendersAddressVersion >= 3: + requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += varintLength + logger.debug('sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s' % requiredAverageProofOfWorkNonceTrialsPerByte) + requiredPayloadLengthExtraBytes, varintLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += varintLength + logger.debug('sender\'s requiredPayloadLengthExtraBytes is %s' % requiredPayloadLengthExtraBytes) + endOfPubkeyPosition = readPosition + + sha = hashlib.new('sha512') + sha.update(sendersPubSigningKey + sendersPubEncryptionKey) + ripe = hashlib.new('ripemd160') + ripe.update(sha.digest()) + + if toRipe != ripe.digest(): + logger.info('The encryption key used to encrypt this message doesn\'t match the keys inbedded in the message itself. Ignoring message.') + return + messageEncodingType, messageEncodingTypeLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + if messageEncodingType == 0: + return + readPosition += messageEncodingTypeLength + messageLength, messageLengthLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + readPosition += messageLengthLength + message = decryptedData[readPosition:readPosition + messageLength] + readPosition += messageLength + readPositionAtBottomOfMessage = readPosition + signatureLength, signatureLengthLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + readPosition += signatureLengthLength + signature = decryptedData[ + readPosition:readPosition + signatureLength] + try: + if not highlevelcrypto.verify(decryptedData[:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')): + logger.debug('ECDSA verify failed') + return + logger.debug('ECDSA verify passed') + except Exception as err: + logger.debug('ECDSA verify failed %s' % err) + return + # verify passed + + # Let's store the public key in case we want to reply to this + # person. + sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', + ripe.digest(), + sendersAddressVersion, + '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition], + int(time.time()), + 'yes') + # shared.workerQueue.put(('newpubkey',(sendersAddressVersion,sendersStream,ripe.digest()))) + # This will check to see whether we happen to be awaiting this + # pubkey in order to send a message. If we are, it will do the POW + # and send it. + self.possibleNewPubkey(ripe=ripe.digest()) + + fromAddress = encodeAddress( + sendersAddressVersion, sendersStream, ripe.digest()) + with shared.printLock: + print 'fromAddress:', fromAddress + + if messageEncodingType == 2: + subject, body = self.decodeType2Message(message) + logger.info('Broadcast subject (first 100 characters): %s' % repr(subject)[:100]) + elif messageEncodingType == 1: + body = message + subject = '' + elif messageEncodingType == 0: + logger.info('messageEncodingType == 0. Doing nothing with the message.') + else: + body = 'Unknown encoding type.\n\n' + repr(message) + subject = '' + + toAddress = '[Broadcast subscribers]' + if messageEncodingType != 0: + + t = (inventoryHash, toAddress, fromAddress, subject, int( + time.time()), body, 'inbox', messageEncodingType, 0) + helper_inbox.insert(t) + + shared.UISignalQueue.put(('displayNewInboxMessage', ( + inventoryHash, toAddress, fromAddress, subject, body))) + + # If we are behaving as an API then we might need to run an + # outside command to let some program know that a new message + # has arrived. + if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + try: + apiNotifyPath = shared.config.get( + 'bitmessagesettings', 'apinotifypath') + except: + apiNotifyPath = '' + if apiNotifyPath != '': + call([apiNotifyPath, "newBroadcast"]) + + # Display timing data + logger.info('Time spent processing this interesting broadcast: %s' % (time.time() - messageProcessingStartTime,)) + + if broadcastVersion == 3: + cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += cleartextStreamNumberLength + embeddedTag = data[readPosition:readPosition+32] + readPosition += 32 + if embeddedTag not in shared.MyECSubscriptionCryptorObjects: + logger.debug('We\'re not interested in this broadcast.') + return + # We are interested in this broadcast because of its tag. + cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag] + try: + decryptedData = cryptorObject.decrypt(data[readPosition:]) + logger.debug('EC decryption successful') + except Exception as err: + logger.debug('Broadcast version 3 decryption Unsuccessful.') + return + + signedBroadcastVersion, readPosition = decodeVarint( + decryptedData[:10]) + beginningOfPubkeyPosition = readPosition # used when we add the pubkey to our pubkey table + sendersAddressVersion, sendersAddressVersionLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + if sendersAddressVersion < 4: + logger.info('Cannot decode senderAddressVersion less than 4 for broadcast version number 3. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.') + return + readPosition += sendersAddressVersionLength + sendersStream, sendersStreamLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + if sendersStream != cleartextStreamNumber: + logger.info('The stream number outside of the encryption on which the POW was completed doesn\'t match the stream number inside the encryption. Ignoring broadcast.') + return + readPosition += sendersStreamLength + behaviorBitfield = decryptedData[readPosition:readPosition + 4] + readPosition += 4 + sendersPubSigningKey = '\x04' + \ + decryptedData[readPosition:readPosition + 64] + readPosition += 64 + sendersPubEncryptionKey = '\x04' + \ + decryptedData[readPosition:readPosition + 64] + readPosition += 64 + if sendersAddressVersion >= 3: + requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += varintLength + logger.debug('sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s' % requiredAverageProofOfWorkNonceTrialsPerByte) + requiredPayloadLengthExtraBytes, varintLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += varintLength + logger.debug('sender\'s requiredPayloadLengthExtraBytes is %s' % requiredPayloadLengthExtraBytes) + endOfPubkeyPosition = readPosition + + sha = hashlib.new('sha512') + sha.update(sendersPubSigningKey + sendersPubEncryptionKey) + ripeHasher = hashlib.new('ripemd160') + ripeHasher.update(sha.digest()) + calculatedRipe = ripeHasher.digest() + + calculatedTag = hashlib.sha512(hashlib.sha512(encodeVarint( + sendersAddressVersion) + encodeVarint(sendersStream) + calculatedRipe).digest()).digest()[32:] + if calculatedTag != embeddedTag: + logger.debug('The tag and encryption key used to encrypt this message doesn\'t match the keys inbedded in the message itself. Ignoring message.') + return + messageEncodingType, messageEncodingTypeLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + if messageEncodingType == 0: + return + readPosition += messageEncodingTypeLength + messageLength, messageLengthLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + readPosition += messageLengthLength + message = decryptedData[readPosition:readPosition + messageLength] + readPosition += messageLength + readPositionAtBottomOfMessage = readPosition + signatureLength, signatureLengthLength = decodeVarint( + decryptedData[readPosition:readPosition + 9]) + readPosition += signatureLengthLength + signature = decryptedData[ + readPosition:readPosition + signatureLength] + try: + if not highlevelcrypto.verify(decryptedData[:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')): + logger.debug('ECDSA verify failed') + return + logger.debug('ECDSA verify passed') + except Exception as err: + logger.debug('ECDSA verify failed %s' % err) + return + # verify passed + + fromAddress = encodeAddress( + sendersAddressVersion, sendersStream, calculatedRipe) + logger.info('fromAddress: %s' % fromAddress) + + # Let's store the public key in case we want to reply to this person. + sqlExecute( + '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', + calculatedRipe, + sendersAddressVersion, + '\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition], + int(time.time()), + 'yes') + # This will check to see whether we happen to be awaiting this + # pubkey in order to send a message. If we are, it will do the + # POW and send it. + self.possibleNewPubkey(address = fromAddress) + + if messageEncodingType == 2: + subject, body = self.decodeType2Message(message) + logger.info('Broadcast subject (first 100 characters): %s' % repr(subject)[:100]) + elif messageEncodingType == 1: + body = message + subject = '' + elif messageEncodingType == 0: + logger.info('messageEncodingType == 0. Doing nothing with the message.') + else: + body = 'Unknown encoding type.\n\n' + repr(message) + subject = '' + + toAddress = '[Broadcast subscribers]' + if messageEncodingType != 0: + + t = (inventoryHash, toAddress, fromAddress, subject, int( + time.time()), body, 'inbox', messageEncodingType, 0) + helper_inbox.insert(t) + + shared.UISignalQueue.put(('displayNewInboxMessage', ( + inventoryHash, toAddress, fromAddress, subject, body))) + + # If we are behaving as an API then we might need to run an + # outside command to let some program know that a new message + # has arrived. + if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + try: + apiNotifyPath = shared.config.get( + 'bitmessagesettings', 'apinotifypath') + except: + apiNotifyPath = '' + if apiNotifyPath != '': + call([apiNotifyPath, "newBroadcast"]) + + # Display timing data + logger.debug('Time spent processing this interesting broadcast: %s' % (time.time() - messageProcessingStartTime,)) + + # We have inserted a pubkey into our pubkey table which we received from a + # pubkey, msg, or broadcast message. It might be one that we have been + # waiting for. Let's check. + def possibleNewPubkey(self, ripe=None, address=None): + # For address versions <= 3, we wait on a key with the correct ripe hash + if ripe != None: + if ripe in shared.neededPubkeys: + logger.info('We have been awaiting the arrival of this pubkey.') + del shared.neededPubkeys[ripe] + sqlExecute( + '''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND (status='awaitingpubkey' or status='doingpubkeypow') and folder='sent' ''', + ripe) + shared.workerQueue.put(('sendmessage', '')) + else: + logger.debug('We don\'t need this pub key. We didn\'t ask for it. Pubkey hash: %s' % ripe.encode('hex')) + # For address versions >= 4, we wait on a pubkey with the correct tag. + # Let us create the tag from the address and see if we were waiting + # for it. + elif address != None: + status, addressVersion, streamNumber, ripe = decodeAddress(address) + tag = hashlib.sha512(hashlib.sha512(encodeVarint( + addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest()[32:] + if tag in shared.neededPubkeys: + logger.info('We have been awaiting the arrival of this pubkey.') + del shared.neededPubkeys[tag] + sqlExecute( + '''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND (status='awaitingpubkey' or status='doingpubkeypow') and folder='sent' ''', + ripe) + shared.workerQueue.put(('sendmessage', '')) + + def ackDataHasAVaildHeader(self, ackData): + if len(ackData) < 24: + logger.info('The length of ackData is unreasonably short. Not sending ackData.') + return False + if ackData[0:4] != '\xe9\xbe\xb4\xd9': + logger.info('Ackdata magic bytes were wrong. Not sending ackData.') + return False + ackDataPayloadLength, = unpack('>L', ackData[16:20]) + if len(ackData) - 24 != ackDataPayloadLength: + logger.info('ackData payload length doesn\'t match the payload length specified in the header. Not sending ackdata.') + return False + if ackData[20:24] != hashlib.sha512(ackData[24:]).digest()[0:4]: # test the checksum in the message. + logger.info('ackdata checksum wrong. Not sending ackdata.') + return False + if ackDataPayloadLength > 180000000: # If the size of the message is greater than 180MB, ignore it. + return False + if (ackData[4:16] != 'getpubkey\x00\x00\x00' and + ackData[4:16] != 'pubkey\x00\x00\x00\x00\x00\x00' and + ackData[4:16] != 'msg\x00\x00\x00\x00\x00\x00\x00\x00\x00' and + ackData[4:16] != 'broadcast\x00\x00\x00'): + return False + return True + + def decodeType2Message(self, message): + bodyPositionIndex = string.find(message, '\nBody:') + if bodyPositionIndex > 1: + subject = message[8:bodyPositionIndex] + # Only save and show the first 500 characters of the subject. + # Any more is probably an attack. + subject = subject[:500] + body = message[bodyPositionIndex + 6:] + else: + subject = '' + body = message + # Throw away any extra lines (headers) after the subject. + if subject: + subject = subject.splitlines()[0] + return subject, body + + def addMailingListNameToSubject(self, subject, mailingListName): + subject = subject.strip() + if subject[:3] == 'Re:' or subject[:3] == 'RE:': + subject = subject[3:].strip() + if '[' + mailingListName + ']' in subject: + return subject + else: + return '[' + mailingListName + '] ' + subject + + def decodeType2Message(self, message): + bodyPositionIndex = string.find(message, '\nBody:') + if bodyPositionIndex > 1: + subject = message[8:bodyPositionIndex] + # Only save and show the first 500 characters of the subject. + # Any more is probably an attack. + subject = subject[:500] + body = message[bodyPositionIndex + 6:] + else: + subject = '' + body = message + # Throw away any extra lines (headers) after the subject. + if subject: + subject = subject.splitlines()[0] + return subject, body \ No newline at end of file diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index c526c9b4..f9343f62 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -113,14 +113,20 @@ class outgoingSynSender(threading.Thread): rd = receiveDataThread() rd.daemon = True # close the main program even if there are threads left someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory. - rd.setup(sock, peer.host, peer.port, self.streamNumber, - someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections) + sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection. + rd.setup(sock, + peer.host, + peer.port, + self.streamNumber, + someObjectsOfWhichThisRemoteNodeIsAlreadyAware, + self.selfInitiatedConnections, + sendDataThreadQueue) rd.start() with shared.printLock: print self, 'connected to', peer, 'during an outgoing attempt.' - sd = sendDataThread() + sd = sendDataThread(sendDataThreadQueue) sd.setup(sock, peer.host, peer.port, self.streamNumber, someObjectsOfWhichThisRemoteNodeIsAlreadyAware) sd.start() diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index b1a62261..bdad9996 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -8,23 +8,23 @@ import socket import random from struct import unpack, pack import sys -import string -from subprocess import call # used when the API must execute an outside program -from pyelliptic.openssl import OpenSSL +#import string +#from subprocess import call # used when the API must execute an outside program +#from pyelliptic.openssl import OpenSSL -import highlevelcrypto +#import highlevelcrypto from addresses import * import helper_generic -import helper_bitcoin -import helper_inbox -import helper_sent +#import helper_bitcoin +#import helper_inbox +#import helper_sent from helper_sql import * -import tr +#import tr from debug import logger #from bitmessagemain import shared.lengthOfTimeToLeaveObjectsInInventory, shared.lengthOfTimeToHoldOnToAllPubkeys, shared.maximumAgeOfAnObjectThatIAmWillingToAccept, shared.maximumAgeOfObjectsThatIAdvertiseToOthers, shared.maximumAgeOfNodesThatIAdvertiseToOthers, shared.numberOfObjectsThatWeHaveYetToGetPerPeer, shared.neededPubkeys # This thread is created either by the synSenderThread(for outgoing -# connections) or the singleListenerThread(for incoming connectiosn). +# connections) or the singleListenerThread(for incoming connections). class receiveDataThread(threading.Thread): @@ -40,14 +40,17 @@ class receiveDataThread(threading.Thread): HOST, port, streamNumber, - someObjectsOfWhichThisRemoteNodeIsAlreadyAware, - selfInitiatedConnections): + someObjectsOfWhichThisRemoteNodeIsAlreadyAware, + selfInitiatedConnections, + sendDataThreadQueue): + self.sock = sock self.peer = shared.Peer(HOST, port) self.streamNumber = streamNumber self.payloadLength = 0 # This is the protocol payload length thus it doesn't include the 24 byte message header self.objectsThatWeHaveYetToGetFromThisPeer = {} self.selfInitiatedConnections = selfInitiatedConnections + self.sendDataThreadQueue = sendDataThreadQueue # used to send commands and data to the sendDataThread shared.connectedHostsList[ self.peer.host] = 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. @@ -56,8 +59,6 @@ class receiveDataThread(threading.Thread): else: self.initiatedConnection = True self.selfInitiatedConnections[streamNumber][self] = 0 - self.ackDataThatWeHaveYetToSend = [ - ] # When we receive a message bound for us, we store the acknowledgement that we need to send (the ackdata) here until we are done processing all other data received from this peer. self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware def run(self): @@ -71,12 +72,10 @@ class receiveDataThread(threading.Thread): except socket.timeout: with shared.printLock: print 'Timeout occurred waiting for data from', self.peer, '. Closing receiveData thread. (ID:', str(id(self)) + ')' - break except Exception as err: with shared.printLock: print 'sock.recv error. Closing receiveData thread (HOST:', self.peer, 'ID:', str(id(self)) + ').', err - break # print 'Received', repr(self.data) if len(self.data) == dataLen: # If self.sock.recv returned no data: @@ -92,7 +91,7 @@ class receiveDataThread(threading.Thread): print 'removed self (a receiveDataThread) from selfInitiatedConnections' except: pass - shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) + shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) # commands the corresponding sendDataThread to shut itself down. try: del shared.connectedHostsList[self.peer.host] except Exception as err: @@ -114,7 +113,7 @@ class receiveDataThread(threading.Thread): # with shared.printLock: # print 'self.data is currently ', repr(self.data) # - if len(self.data) < 20: # if so little of the data has arrived that we can't even unpack the payload length + if len(self.data) < 24: # if so little of the data has arrived that we can't even read the checksum then wait for more data. return if self.data[0:4] != '\xe9\xbe\xb4\xd9': if shared.verbose >= 1: @@ -150,7 +149,7 @@ class receiveDataThread(threading.Thread): elif remoteCommand == 'addr\x00\x00\x00\x00\x00\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: self.recaddr(self.data[24:self.payloadLength + 24]) elif remoteCommand == 'getpubkey\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: - self.recgetpubkey(self.data[24:self.payloadLength + 24]) + shared.checkAndSharegetpubkeyWithPeers(self.data[24:self.payloadLength + 24]) elif remoteCommand == 'pubkey\x00\x00\x00\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: self.recpubkey(self.data[24:self.payloadLength + 24]) elif remoteCommand == 'inv\x00\x00\x00\x00\x00\x00\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: @@ -191,7 +190,7 @@ class receiveDataThread(threading.Thread): else: self.sendgetdata(objectHash) del self.objectsThatWeHaveYetToGetFromThisPeer[ - objectHash] # It is possible that the remote node doesn't respond with the object. In that case, we'll very likely get it from someone else anyway. + objectHash] # It is possible that the remote node might not respond with the object. In that case, we'll very likely get it from someone else anyway. if len(self.objectsThatWeHaveYetToGetFromThisPeer) == 0: with shared.printLock: print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToGetFromThisPeer is now', len(self.objectsThatWeHaveYetToGetFromThisPeer) @@ -217,33 +216,12 @@ class receiveDataThread(threading.Thread): shared.numberOfObjectsThatWeHaveYetToGetPerPeer[self.peer] = len( self.objectsThatWeHaveYetToGetFromThisPeer) # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. - if len(self.ackDataThatWeHaveYetToSend) > 0: - self.data = self.ackDataThatWeHaveYetToSend.pop() self.processData() - def isProofOfWorkSufficient( - self, - data, - nonceTrialsPerByte=0, - payloadLengthExtraBytes=0): - if nonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte: - nonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte - if payloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes: - payloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes - POW, = unpack('>Q', hashlib.sha512(hashlib.sha512(data[ - :8] + hashlib.sha512(data[8:]).digest()).digest()).digest()[0:8]) - # print 'POW:', POW - return POW <= 2 ** 64 / ((len(data) + payloadLengthExtraBytes) * (nonceTrialsPerByte)) def sendpong(self): print 'Sending pong' - try: - self.sock.sendall( - '\xE9\xBE\xB4\xD9\x70\x6F\x6E\x67\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcf\x83\xe1\x35') - except Exception as err: - # if not 'Bad file descriptor' in err: - with shared.printLock: - print 'sock.sendall error:', err + self.sendDataThreadQueue.put((0, 'sendRawData', '\xE9\xBE\xB4\xD9\x70\x6F\x6E\x67\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcf\x83\xe1\x35')) def recverack(self): @@ -267,8 +245,7 @@ class receiveDataThread(threading.Thread): print 'The length of sendDataQueues is now:', len(shared.sendDataQueues) print 'broadcasting addr from within connectionFullyEstablished function.' - #self.broadcastaddr([(int(time.time()), self.streamNumber, 1, self.peer.host, - # self.remoteNodeIncomingPort)]) # This lets all of our peers know about this new node. + # Let all of our peers know about this new node. dataToSend = (int(time.time()), self.streamNumber, 1, self.peer.host, self.remoteNodeIncomingPort) shared.broadcastToSendDataQueues(( self.streamNumber, 'advertisepeer', dataToSend)) @@ -329,83 +306,25 @@ class receiveDataThread(threading.Thread): headerData += hashlib.sha512(payload).digest()[:4] with shared.printLock: print 'Sending huge inv message with', numberOfObjects, 'objects to just this one peer' - - try: - self.sock.sendall(headerData + payload) - except Exception as err: - # if not 'Bad file descriptor' in err: - with shared.printLock: - print 'sock.sendall error:', err + self.sendDataThreadQueue.put((0, 'sendRawData', headerData + payload)) # We have received a broadcast message def recbroadcast(self, data): self.messageProcessingStartTime = time.time() - # First we must check to make sure the proof of work is sufficient. - if not self.isProofOfWorkSufficient(data): - print 'Proof of work in broadcast message insufficient.' - return - readPosition = 8 # bypass the nonce - embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) - # This section is used for the transition from 32 bit time to 64 bit - # time in the protocol. - if embeddedTime == 0: - embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) - readPosition += 8 - else: - readPosition += 4 + shared.checkAndShareBroadcastWithPeers(data) - if embeddedTime > (int(time.time()) + 10800): # prevent funny business - print 'The embedded time in this broadcast message is more than three hours in the future. That doesn\'t make sense. Ignoring message.' - return - if embeddedTime < (int(time.time()) - shared.maximumAgeOfAnObjectThatIAmWillingToAccept): - print 'The embedded time in this broadcast message is too old. Ignoring message.' - return - if len(data) < 180: - print 'The payload length of this broadcast packet is unreasonably low. Someone is probably trying funny business. Ignoring message.' - return - # Let us check to make sure the stream number is correct (thus - # preventing an individual from sending broadcasts out on the wrong - # streams or all streams). - broadcastVersion, broadcastVersionLength = decodeVarint( - data[readPosition:readPosition + 10]) - if broadcastVersion >= 2: - streamNumber, streamNumberLength = decodeVarint(data[ - readPosition + broadcastVersionLength:readPosition + broadcastVersionLength + 10]) - if streamNumber != self.streamNumber: - print 'The stream number encoded in this broadcast message (' + str(streamNumber) + ') does not match the stream number on which it was received. Ignoring it.' - return - - shared.numberOfInventoryLookupsPerformed += 1 - shared.inventoryLock.acquire() - self.inventoryHash = calculateInventoryHash(data) - if self.inventoryHash in shared.inventory: - print 'We have already received this broadcast object. Ignoring.' - shared.inventoryLock.release() - return - elif shared.isInSqlInventory(self.inventoryHash): - print 'We have already received this broadcast object (it is stored on disk in the SQL inventory). Ignoring it.' - shared.inventoryLock.release() - return - # It is valid so far. Let's let our peers know about it. - objectType = 'broadcast' - shared.inventory[self.inventoryHash] = ( - objectType, self.streamNumber, data, embeddedTime,'') - shared.inventorySets[self.streamNumber].add(self.inventoryHash) - shared.inventoryLock.release() - self.broadcastinv(self.inventoryHash) - shared.numberOfBroadcastsProcessed += 1 - shared.UISignalQueue.put(( - 'updateNumberOfBroadcastsProcessed', 'no data')) - - self.processbroadcast( - readPosition, data) # When this function returns, we will have either successfully processed this broadcast because we are interested in it, ignored it because we aren't interested in it, or found problem with the broadcast that warranted ignoring it. - - # Let us now set lengthOfTimeWeShouldUseToProcessThisMessage. If we - # haven't used the specified amount of time, we shall sleep. These - # values are mostly the same values used for msg messages although - # broadcast messages are processed faster. + """ + Let us now set lengthOfTimeWeShouldUseToProcessThisMessage. 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. + """ if len(data) > 100000000: # Size is greater than 100 megabytes lengthOfTimeWeShouldUseToProcessThisMessage = 100 # seconds. elif len(data) > 10000000: # Between 100 and 10 megabytes @@ -420,501 +339,24 @@ class receiveDataThread(threading.Thread): if sleepTime > 0 and doTimingAttackMitigation: with shared.printLock: print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.' - time.sleep(sleepTime) - with shared.printLock: - print 'Total message processing time:', time.time() - self.messageProcessingStartTime, 'seconds.' - - - # A broadcast message has a valid time and POW and requires processing. - # The recbroadcast function calls this one. - def processbroadcast(self, readPosition, data): - broadcastVersion, broadcastVersionLength = decodeVarint( - data[readPosition:readPosition + 9]) - readPosition += broadcastVersionLength - if broadcastVersion < 1 or broadcastVersion > 3: - print 'Cannot decode incoming broadcast versions higher than 3. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.' - return - if broadcastVersion == 1: - beginningOfPubkeyPosition = readPosition # used when we add the pubkey to our pubkey table - sendersAddressVersion, sendersAddressVersionLength = decodeVarint( - data[readPosition:readPosition + 9]) - if sendersAddressVersion <= 1 or sendersAddressVersion >= 3: - # Cannot decode senderAddressVersion higher than 2. Assuming - # the sender isn\'t being silly, you should upgrade Bitmessage - # because this message shall be ignored. - return - readPosition += sendersAddressVersionLength - if sendersAddressVersion == 2: - sendersStream, sendersStreamLength = decodeVarint( - data[readPosition:readPosition + 9]) - readPosition += sendersStreamLength - behaviorBitfield = data[readPosition:readPosition + 4] - readPosition += 4 - sendersPubSigningKey = '\x04' + \ - data[readPosition:readPosition + 64] - readPosition += 64 - sendersPubEncryptionKey = '\x04' + \ - data[readPosition:readPosition + 64] - readPosition += 64 - endOfPubkeyPosition = readPosition - sendersHash = data[readPosition:readPosition + 20] - if sendersHash not in shared.broadcastSendersForWhichImWatching: - # Display timing data - with shared.printLock: - print 'Time spent deciding that we are not interested in this v1 broadcast:', time.time() - self.messageProcessingStartTime - - return - # At this point, this message claims to be from sendersHash and - # we are interested in it. We still have to hash the public key - # to make sure it is truly the key that matches the hash, and - # also check the signiture. - readPosition += 20 - - sha = hashlib.new('sha512') - sha.update(sendersPubSigningKey + sendersPubEncryptionKey) - ripe = hashlib.new('ripemd160') - ripe.update(sha.digest()) - if ripe.digest() != sendersHash: - # The sender of this message lied. - return - messageEncodingType, messageEncodingTypeLength = decodeVarint( - data[readPosition:readPosition + 9]) - if messageEncodingType == 0: - return - readPosition += messageEncodingTypeLength - messageLength, messageLengthLength = decodeVarint( - data[readPosition:readPosition + 9]) - readPosition += messageLengthLength - message = data[readPosition:readPosition + messageLength] - readPosition += messageLength - readPositionAtBottomOfMessage = readPosition - signatureLength, signatureLengthLength = decodeVarint( - data[readPosition:readPosition + 9]) - readPosition += signatureLengthLength - signature = data[readPosition:readPosition + signatureLength] - try: - if not highlevelcrypto.verify(data[12:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')): - print 'ECDSA verify failed' - return - print 'ECDSA verify passed' - except Exception as err: - print 'ECDSA verify failed', err - return - # verify passed - fromAddress = encodeAddress( - sendersAddressVersion, sendersStream, ripe.digest()) - with shared.printLock: - print 'fromAddress:', fromAddress - - # Let's store the public key in case we want to reply to this person. - # We don't have the correct nonce or time (which would let us - # send out a pubkey message) so we'll just fill it with 1's. We - # won't be able to send this pubkey to others (without doing - # the proof of work ourselves, which this program is programmed - # to not do.) - sqlExecute( - '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', - ripe.digest(), - sendersAddressVersion, - '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + data[beginningOfPubkeyPosition:endOfPubkeyPosition], - int(time.time()), - 'yes') - # This will check to see whether we happen to be awaiting this - # pubkey in order to send a message. If we are, it will do the - # POW and send it. - self.possibleNewPubkey(ripe=ripe.digest()) - - - if messageEncodingType == 2: - subject, body = self.decodeType2Message(message) - elif messageEncodingType == 1: - body = message - subject = '' - elif messageEncodingType == 0: - print 'messageEncodingType == 0. Doing nothing with the message.' - else: - body = 'Unknown encoding type.\n\n' + repr(message) - subject = '' - - toAddress = '[Broadcast subscribers]' - if messageEncodingType != 0: - - t = (self.inventoryHash, toAddress, fromAddress, subject, int( - time.time()), body, 'inbox', messageEncodingType, 0) - helper_inbox.insert(t) - - shared.UISignalQueue.put(('displayNewInboxMessage', ( - self.inventoryHash, toAddress, fromAddress, subject, body))) - - # If we are behaving as an API then we might need to run an - # outside command to let some program know that a new - # message has arrived. - if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): - try: - apiNotifyPath = shared.config.get( - 'bitmessagesettings', 'apinotifypath') - except: - apiNotifyPath = '' - if apiNotifyPath != '': - call([apiNotifyPath, "newBroadcast"]) - - # Display timing data - with shared.printLock: - print 'Time spent processing this interesting broadcast:', time.time() - self.messageProcessingStartTime - - if broadcastVersion == 2: - cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += cleartextStreamNumberLength - initialDecryptionSuccessful = False - for key, cryptorObject in shared.MyECSubscriptionCryptorObjects.items(): - try: - decryptedData = cryptorObject.decrypt(data[readPosition:]) - toRipe = key # This is the RIPE hash of the sender's pubkey. We need this below to compare to the RIPE hash of the sender's address to verify that it was encrypted by with their key rather than some other key. - initialDecryptionSuccessful = True - print 'EC decryption successful using key associated with ripe hash:', key.encode('hex') - break - except Exception as err: - pass - # print 'cryptorObject.decrypt Exception:', err - if not initialDecryptionSuccessful: - # This is not a broadcast I am interested in. - with shared.printLock: - print 'Length of time program spent failing to decrypt this v2 broadcast:', time.time() - self.messageProcessingStartTime, 'seconds.' - - return - # At this point this is a broadcast I have decrypted and thus am - # interested in. - signedBroadcastVersion, readPosition = decodeVarint( - decryptedData[:10]) - beginningOfPubkeyPosition = readPosition # used when we add the pubkey to our pubkey table - sendersAddressVersion, sendersAddressVersionLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - if sendersAddressVersion < 2 or sendersAddressVersion > 3: - print 'Cannot decode senderAddressVersion other than 2 or 3. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.' - return - readPosition += sendersAddressVersionLength - sendersStream, sendersStreamLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - if sendersStream != cleartextStreamNumber: - print 'The stream number outside of the encryption on which the POW was completed doesn\'t match the stream number inside the encryption. Ignoring broadcast.' - return - readPosition += sendersStreamLength - behaviorBitfield = decryptedData[readPosition:readPosition + 4] - readPosition += 4 - sendersPubSigningKey = '\x04' + \ - decryptedData[readPosition:readPosition + 64] - readPosition += 64 - sendersPubEncryptionKey = '\x04' + \ - decryptedData[readPosition:readPosition + 64] - readPosition += 64 - if sendersAddressVersion >= 3: - requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += varintLength - print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte - requiredPayloadLengthExtraBytes, varintLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += varintLength - print 'sender\'s requiredPayloadLengthExtraBytes is', requiredPayloadLengthExtraBytes - endOfPubkeyPosition = readPosition - - sha = hashlib.new('sha512') - sha.update(sendersPubSigningKey + sendersPubEncryptionKey) - ripe = hashlib.new('ripemd160') - ripe.update(sha.digest()) - - if toRipe != ripe.digest(): - print 'The encryption key used to encrypt this message doesn\'t match the keys inbedded in the message itself. Ignoring message.' - return - messageEncodingType, messageEncodingTypeLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - if messageEncodingType == 0: - return - readPosition += messageEncodingTypeLength - messageLength, messageLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - readPosition += messageLengthLength - message = decryptedData[readPosition:readPosition + messageLength] - readPosition += messageLength - readPositionAtBottomOfMessage = readPosition - signatureLength, signatureLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - readPosition += signatureLengthLength - signature = decryptedData[ - readPosition:readPosition + signatureLength] - try: - if not highlevelcrypto.verify(decryptedData[:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')): - print 'ECDSA verify failed' - return - print 'ECDSA verify passed' - except Exception as err: - print 'ECDSA verify failed', err - return - # verify passed - - # Let's store the public key in case we want to reply to this - # person. - sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', - ripe.digest(), - sendersAddressVersion, - '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition], - int(time.time()), - 'yes') - # shared.workerQueue.put(('newpubkey',(sendersAddressVersion,sendersStream,ripe.digest()))) - # This will check to see whether we happen to be awaiting this - # pubkey in order to send a message. If we are, it will do the POW - # and send it. - self.possibleNewPubkey(ripe=ripe.digest()) - - fromAddress = encodeAddress( - sendersAddressVersion, sendersStream, ripe.digest()) - with shared.printLock: - print 'fromAddress:', fromAddress - - if messageEncodingType == 2: - subject, body = self.decodeType2Message(message) - elif messageEncodingType == 1: - body = message - subject = '' - elif messageEncodingType == 0: - print 'messageEncodingType == 0. Doing nothing with the message.' - else: - body = 'Unknown encoding type.\n\n' + repr(message) - subject = '' - - toAddress = '[Broadcast subscribers]' - if messageEncodingType != 0: - - t = (self.inventoryHash, toAddress, fromAddress, subject, int( - time.time()), body, 'inbox', messageEncodingType, 0) - helper_inbox.insert(t) - - shared.UISignalQueue.put(('displayNewInboxMessage', ( - self.inventoryHash, toAddress, fromAddress, subject, body))) - - # If we are behaving as an API then we might need to run an - # outside command to let some program know that a new message - # has arrived. - if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): - try: - apiNotifyPath = shared.config.get( - 'bitmessagesettings', 'apinotifypath') - except: - apiNotifyPath = '' - if apiNotifyPath != '': - call([apiNotifyPath, "newBroadcast"]) - - # Display timing data - with shared.printLock: - print 'Time spent processing this interesting broadcast:', time.time() - self.messageProcessingStartTime - - if broadcastVersion == 3: - cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += cleartextStreamNumberLength - embeddedTag = data[readPosition:readPosition+32] - readPosition += 32 - if embeddedTag not in shared.MyECSubscriptionCryptorObjects: - with shared.printLock: - print 'We\'re not interested in this broadcast.' - return - # We are interested in this broadcast because of its tag. - cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag] - try: - decryptedData = cryptorObject.decrypt(data[readPosition:]) - print 'EC decryption successful' - except Exception as err: - with shared.printLock: - print 'Broadcast version 3 decryption Unsuccessful.' - return - - signedBroadcastVersion, readPosition = decodeVarint( - decryptedData[:10]) - beginningOfPubkeyPosition = readPosition # used when we add the pubkey to our pubkey table - sendersAddressVersion, sendersAddressVersionLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - if sendersAddressVersion < 4: - print 'Cannot decode senderAddressVersion less than 4 for broadcast version number 3. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.' - return - readPosition += sendersAddressVersionLength - sendersStream, sendersStreamLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - if sendersStream != cleartextStreamNumber: - print 'The stream number outside of the encryption on which the POW was completed doesn\'t match the stream number inside the encryption. Ignoring broadcast.' - return - readPosition += sendersStreamLength - behaviorBitfield = decryptedData[readPosition:readPosition + 4] - readPosition += 4 - sendersPubSigningKey = '\x04' + \ - decryptedData[readPosition:readPosition + 64] - readPosition += 64 - sendersPubEncryptionKey = '\x04' + \ - decryptedData[readPosition:readPosition + 64] - readPosition += 64 - if sendersAddressVersion >= 3: - requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += varintLength - print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte - requiredPayloadLengthExtraBytes, varintLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += varintLength - print 'sender\'s requiredPayloadLengthExtraBytes is', requiredPayloadLengthExtraBytes - endOfPubkeyPosition = readPosition - - sha = hashlib.new('sha512') - sha.update(sendersPubSigningKey + sendersPubEncryptionKey) - ripeHasher = hashlib.new('ripemd160') - ripeHasher.update(sha.digest()) - calculatedRipe = ripeHasher.digest() - - calculatedTag = hashlib.sha512(hashlib.sha512(encodeVarint( - sendersAddressVersion) + encodeVarint(sendersStream) + calculatedRipe).digest()).digest()[32:] - if calculatedTag != embeddedTag: - print 'The tag and encryption key used to encrypt this message doesn\'t match the keys inbedded in the message itself. Ignoring message.' - return - messageEncodingType, messageEncodingTypeLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - if messageEncodingType == 0: - return - readPosition += messageEncodingTypeLength - messageLength, messageLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - readPosition += messageLengthLength - message = decryptedData[readPosition:readPosition + messageLength] - readPosition += messageLength - readPositionAtBottomOfMessage = readPosition - signatureLength, signatureLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 9]) - readPosition += signatureLengthLength - signature = decryptedData[ - readPosition:readPosition + signatureLength] - try: - if not highlevelcrypto.verify(decryptedData[:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')): - print 'ECDSA verify failed' - return - print 'ECDSA verify passed' - except Exception as err: - print 'ECDSA verify failed', err - return - # verify passed - - fromAddress = encodeAddress( - sendersAddressVersion, sendersStream, calculatedRipe) - with shared.printLock: - print 'fromAddress:', fromAddress - - # Let's store the public key in case we want to reply to this person. - sqlExecute( - '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', - calculatedRipe, - sendersAddressVersion, - '\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition], - int(time.time()), - 'yes') - # This will check to see whether we happen to be awaiting this - # pubkey in order to send a message. If we are, it will do the - # POW and send it. - self.possibleNewPubkey(address = fromAddress) - - if messageEncodingType == 2: - subject, body = self.decodeType2Message(message) - elif messageEncodingType == 1: - body = message - subject = '' - elif messageEncodingType == 0: - print 'messageEncodingType == 0. Doing nothing with the message.' - else: - body = 'Unknown encoding type.\n\n' + repr(message) - subject = '' - - toAddress = '[Broadcast subscribers]' - if messageEncodingType != 0: - - t = (self.inventoryHash, toAddress, fromAddress, subject, int( - time.time()), body, 'inbox', messageEncodingType, 0) - helper_inbox.insert(t) - - shared.UISignalQueue.put(('displayNewInboxMessage', ( - self.inventoryHash, toAddress, fromAddress, subject, body))) - - # If we are behaving as an API then we might need to run an - # outside command to let some program know that a new message - # has arrived. - if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): - try: - apiNotifyPath = shared.config.get( - 'bitmessagesettings', 'apinotifypath') - except: - apiNotifyPath = '' - if apiNotifyPath != '': - call([apiNotifyPath, "newBroadcast"]) - - # Display timing data - with shared.printLock: - print 'Time spent processing this interesting broadcast:', time.time() - self.messageProcessingStartTime # We have received a msg message. def recmsg(self, data): self.messageProcessingStartTime = time.time() - # First we must check to make sure the proof of work is sufficient. - if not self.isProofOfWorkSufficient(data): - print 'Proof of work in msg message insufficient.' - return - readPosition = 8 - embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) + shared.checkAndShareMsgWithPeers(data) - # This section is used for the transition from 32 bit time to 64 bit - # time in the protocol. - if embeddedTime == 0: - embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) - readPosition += 8 - else: - readPosition += 4 - - if embeddedTime > int(time.time()) + 10800: - print 'The time in the msg message is too new. Ignoring it. Time:', embeddedTime - return - if embeddedTime < int(time.time()) - shared.maximumAgeOfAnObjectThatIAmWillingToAccept: - print 'The time in the msg message is too old. Ignoring it. Time:', embeddedTime - return - streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = decodeVarint( - data[readPosition:readPosition + 9]) - if streamNumberAsClaimedByMsg != self.streamNumber: - print 'The stream number encoded in this msg (' + str(streamNumberAsClaimedByMsg) + ') message does not match the stream number on which it was received. Ignoring it.' - return - readPosition += streamNumberAsClaimedByMsgLength - self.inventoryHash = calculateInventoryHash(data) - shared.numberOfInventoryLookupsPerformed += 1 - shared.inventoryLock.acquire() - if self.inventoryHash in shared.inventory: - print 'We have already received this msg message. Ignoring.' - shared.inventoryLock.release() - return - elif shared.isInSqlInventory(self.inventoryHash): - print 'We have already received this msg message (it is stored on disk in the SQL inventory). Ignoring it.' - shared.inventoryLock.release() - return - # This msg message is valid. Let's let our peers know about it. - objectType = 'msg' - shared.inventory[self.inventoryHash] = ( - objectType, self.streamNumber, data, embeddedTime,'') - shared.inventorySets[self.streamNumber].add(self.inventoryHash) - shared.inventoryLock.release() - self.broadcastinv(self.inventoryHash) - shared.numberOfMessagesProcessed += 1 - shared.UISignalQueue.put(( - 'updateNumberOfMessagesProcessed', 'no data')) - - self.processmsg( - readPosition, data) # When this function returns, we will have either successfully processed the message bound for us, ignored it because it isn't bound for us, or found problem with the message that warranted ignoring it. - - # Let us now set lengthOfTimeWeShouldUseToProcessThisMessage. If we - # haven't used the specified amount of time, we shall sleep. These - # values are based on test timings and you may change them at-will. + """ + Let us now set lengthOfTimeWeShouldUseToProcessThisMessage. 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. + """ if len(data) > 100000000: # Size is greater than 100 megabytes lengthOfTimeWeShouldUseToProcessThisMessage = 100 # seconds. Actual length of time it took my computer to decrypt and verify the signature of a 100 MB message: 3.7 seconds. elif len(data) > 10000000: # Between 100 and 10 megabytes @@ -929,422 +371,13 @@ class receiveDataThread(threading.Thread): if sleepTime > 0 and doTimingAttackMitigation: with shared.printLock: print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.' - time.sleep(sleepTime) - with shared.printLock: - print 'Total message processing time:', time.time() - self.messageProcessingStartTime, 'seconds.' - - - # A msg message has a valid time and POW and requires processing. The - # recmsg function calls this one. - def processmsg(self, readPosition, encryptedData): - initialDecryptionSuccessful = False - # Let's check whether this is a message acknowledgement bound for us. - if encryptedData[readPosition:] in shared.ackdataForWhichImWatching: - with shared.printLock: - print 'This msg IS an acknowledgement bound for me.' - - del shared.ackdataForWhichImWatching[encryptedData[readPosition:]] - sqlExecute('UPDATE sent SET status=? WHERE ackdata=?', - 'ackreceived', encryptedData[readPosition:]) - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (encryptedData[readPosition:], tr.translateText("MainWindow",'Acknowledgement of the message received. %1').arg(unicode( - time.strftime(shared.config.get('bitmessagesettings', 'timeformat'), time.localtime(int(time.time()))), 'utf-8'))))) - return - else: - with shared.printLock: - print 'This was NOT an acknowledgement bound for me.' - # print 'shared.ackdataForWhichImWatching', shared.ackdataForWhichImWatching - - - # This is not an acknowledgement bound for me. See if it is a message - # bound for me by trying to decrypt it with my private keys. - for key, cryptorObject in shared.myECCryptorObjects.items(): - try: - decryptedData = cryptorObject.decrypt( - encryptedData[readPosition:]) - toRipe = key # This is the RIPE hash of my pubkeys. We need this below to compare to the destination_ripe included in the encrypted data. - initialDecryptionSuccessful = True - with shared.printLock: - print 'EC decryption successful using key associated with ripe hash:', key.encode('hex') - break - except Exception as err: - pass - # print 'cryptorObject.decrypt Exception:', err - if not initialDecryptionSuccessful: - # This is not a message bound for me. - with shared.printLock: - print 'Length of time program spent failing to decrypt this message:', time.time() - self.messageProcessingStartTime, 'seconds.' - - else: - # This is a message bound for me. - toAddress = shared.myAddressesByHash[ - toRipe] # Look up my address based on the RIPE hash. - readPosition = 0 - messageVersion, messageVersionLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += messageVersionLength - if messageVersion != 1: - print 'Cannot understand message versions other than one. Ignoring message.' - return - sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += sendersAddressVersionNumberLength - if sendersAddressVersionNumber == 0: - print 'Cannot understand sendersAddressVersionNumber = 0. Ignoring message.' - return - if sendersAddressVersionNumber > 4: - print 'Sender\'s address version number', sendersAddressVersionNumber, 'not yet supported. Ignoring message.' - return - if len(decryptedData) < 170: - print 'Length of the unencrypted data is unreasonably short. Sanity check failed. Ignoring message.' - return - sendersStreamNumber, sendersStreamNumberLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - if sendersStreamNumber == 0: - print 'sender\'s stream number is 0. Ignoring message.' - return - readPosition += sendersStreamNumberLength - behaviorBitfield = decryptedData[readPosition:readPosition + 4] - readPosition += 4 - pubSigningKey = '\x04' + decryptedData[ - readPosition:readPosition + 64] - readPosition += 64 - pubEncryptionKey = '\x04' + decryptedData[ - readPosition:readPosition + 64] - readPosition += 64 - if sendersAddressVersionNumber >= 3: - requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += varintLength - print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte - requiredPayloadLengthExtraBytes, varintLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += varintLength - print 'sender\'s requiredPayloadLengthExtraBytes is', requiredPayloadLengthExtraBytes - endOfThePublicKeyPosition = readPosition # needed for when we store the pubkey in our database of pubkeys for later use. - if toRipe != decryptedData[readPosition:readPosition + 20]: - with shared.printLock: - print 'The original sender of this message did not send it to you. Someone is attempting a Surreptitious Forwarding Attack.' - print 'See: http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html' - print 'your toRipe:', toRipe.encode('hex') - print 'embedded destination toRipe:', decryptedData[readPosition:readPosition + 20].encode('hex') - - return - readPosition += 20 - messageEncodingType, messageEncodingTypeLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += messageEncodingTypeLength - messageLength, messageLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += messageLengthLength - message = decryptedData[readPosition:readPosition + messageLength] - # print 'First 150 characters of message:', repr(message[:150]) - readPosition += messageLength - ackLength, ackLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += ackLengthLength - ackData = decryptedData[readPosition:readPosition + ackLength] - readPosition += ackLength - positionOfBottomOfAckData = readPosition # needed to mark the end of what is covered by the signature - signatureLength, signatureLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += signatureLengthLength - signature = decryptedData[ - readPosition:readPosition + signatureLength] - try: - if not highlevelcrypto.verify(decryptedData[:positionOfBottomOfAckData], signature, pubSigningKey.encode('hex')): - print 'ECDSA verify failed' - return - print 'ECDSA verify passed' - except Exception as err: - print 'ECDSA verify failed', err - return - with shared.printLock: - print 'As a matter of intellectual curiosity, here is the Bitcoin address associated with the keys owned by the other person:', helper_bitcoin.calculateBitcoinAddressFromPubkey(pubSigningKey), ' ..and here is the testnet address:', helper_bitcoin.calculateTestnetAddressFromPubkey(pubSigningKey), '. The other person must take their private signing key from Bitmessage and import it into Bitcoin (or a service like Blockchain.info) for it to be of any use. Do not use this unless you know what you are doing.' - - # calculate the fromRipe. - sha = hashlib.new('sha512') - sha.update(pubSigningKey + pubEncryptionKey) - ripe = hashlib.new('ripemd160') - ripe.update(sha.digest()) - fromAddress = encodeAddress( - sendersAddressVersionNumber, sendersStreamNumber, ripe.digest()) - # Let's store the public key in case we want to reply to this - # person. - if sendersAddressVersionNumber <= 3: - sqlExecute( - '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', - ripe.digest(), - sendersAddressVersionNumber, - '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[messageVersionLength:endOfThePublicKeyPosition], - int(time.time()), - 'yes') - # This will check to see whether we happen to be awaiting this - # pubkey in order to send a message. If we are, it will do the POW - # and send it. - self.possibleNewPubkey(ripe=ripe.digest()) - elif sendersAddressVersionNumber >= 4: - sqlExecute( - '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', - ripe.digest(), - sendersAddressVersionNumber, - '\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[messageVersionLength:endOfThePublicKeyPosition], - int(time.time()), - 'yes') - # This will check to see whether we happen to be awaiting this - # pubkey in order to send a message. If we are, it will do the POW - # and send it. - self.possibleNewPubkey(address = fromAddress) - # If this message is bound for one of my version 3 addresses (or - # higher), then we must check to make sure it meets our demanded - # proof of work requirement. - if decodeAddress(toAddress)[1] >= 3: # If the toAddress version number is 3 or higher: - if not shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(fromAddress): # If I'm not friendly with this person: - requiredNonceTrialsPerByte = shared.config.getint( - toAddress, 'noncetrialsperbyte') - requiredPayloadLengthExtraBytes = shared.config.getint( - toAddress, 'payloadlengthextrabytes') - if not self.isProofOfWorkSufficient(encryptedData, requiredNonceTrialsPerByte, requiredPayloadLengthExtraBytes): - print 'Proof of work in msg message insufficient only because it does not meet our higher requirement.' - return - blockMessage = False # Gets set to True if the user shouldn't see the message according to black or white lists. - if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': # If we are using a blacklist - queryreturn = sqlQuery( - '''SELECT label FROM blacklist where address=? and enabled='1' ''', - fromAddress) - if queryreturn != []: - with shared.printLock: - print 'Message ignored because address is in blacklist.' - - blockMessage = True - else: # We're using a whitelist - queryreturn = sqlQuery( - '''SELECT label FROM whitelist where address=? and enabled='1' ''', - fromAddress) - if queryreturn == []: - print 'Message ignored because address not in whitelist.' - blockMessage = True - if not blockMessage: - print 'fromAddress:', fromAddress - print 'First 150 characters of message:', repr(message[:150]) - - toLabel = shared.config.get(toAddress, 'label') - if toLabel == '': - toLabel = toAddress - - if messageEncodingType == 2: - subject, body = self.decodeType2Message(message) - elif messageEncodingType == 1: - body = message - subject = '' - elif messageEncodingType == 0: - print 'messageEncodingType == 0. Doing nothing with the message. They probably just sent it so that we would store their public key or send their ack data for them.' - else: - body = 'Unknown encoding type.\n\n' + repr(message) - subject = '' - if messageEncodingType != 0: - t = (self.inventoryHash, toAddress, fromAddress, subject, int( - time.time()), body, 'inbox', messageEncodingType, 0) - helper_inbox.insert(t) - - shared.UISignalQueue.put(('displayNewInboxMessage', ( - self.inventoryHash, toAddress, fromAddress, subject, body))) - - # If we are behaving as an API then we might need to run an - # outside command to let some program know that a new message - # has arrived. - if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): - try: - apiNotifyPath = shared.config.get( - 'bitmessagesettings', 'apinotifypath') - except: - apiNotifyPath = '' - if apiNotifyPath != '': - call([apiNotifyPath, "newMessage"]) - - # Let us now check and see whether our receiving address is - # behaving as a mailing list - if shared.safeConfigGetBoolean(toAddress, 'mailinglist'): - try: - mailingListName = shared.config.get( - toAddress, 'mailinglistname') - except: - mailingListName = '' - # Let us send out this message as a broadcast - subject = self.addMailingListNameToSubject( - subject, mailingListName) - # Let us now send this message out as a broadcast - message = time.strftime("%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime( - )) + ' Message ostensibly from ' + fromAddress + ':\n\n' + body - fromAddress = toAddress # The fromAddress for the broadcast that we are about to send is the toAddress (my address) for the msg message we are currently processing. - ackdata = OpenSSL.rand( - 32) # We don't actually need the ackdata for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating. - toAddress = '[Broadcast subscribers]' - ripe = '' - - t = ('', toAddress, ripe, fromAddress, subject, message, ackdata, int( - time.time()), 'broadcastqueued', 1, 1, 'sent', 2) - helper_sent.insert(t) - - shared.UISignalQueue.put(('displayNewSentMessage', ( - toAddress, '[Broadcast subscribers]', fromAddress, subject, message, ackdata))) - shared.workerQueue.put(('sendbroadcast', '')) - - if self.isAckDataValid(ackData): - print 'ackData is valid. Will process it.' - self.ackDataThatWeHaveYetToSend.append( - ackData) # When we have processed all data, the processData function will pop the ackData out and process it as if it is a message received from our peer. - # Display timing data - timeRequiredToAttemptToDecryptMessage = time.time( - ) - self.messageProcessingStartTime - shared.successfullyDecryptMessageTimings.append( - timeRequiredToAttemptToDecryptMessage) - sum = 0 - for item in shared.successfullyDecryptMessageTimings: - sum += item - with shared.printLock: - print 'Time to decrypt this message successfully:', timeRequiredToAttemptToDecryptMessage - print 'Average time for all message decryption successes since startup:', sum / len(shared.successfullyDecryptMessageTimings) - - def decodeType2Message(self, message): - bodyPositionIndex = string.find(message, '\nBody:') - if bodyPositionIndex > 1: - subject = message[8:bodyPositionIndex] - # Only save and show the first 500 characters of the subject. - # Any more is probably an attack. - subject = subject[:500] - body = message[bodyPositionIndex + 6:] - else: - subject = '' - body = message - # Throw away any extra lines (headers) after the subject. - if subject: - subject = subject.splitlines()[0] - return subject, body - - def isAckDataValid(self, ackData): - if len(ackData) < 24: - print 'The length of ackData is unreasonably short. Not sending ackData.' - return False - if ackData[0:4] != '\xe9\xbe\xb4\xd9': - print 'Ackdata magic bytes were wrong. Not sending ackData.' - return False - ackDataPayloadLength, = unpack('>L', ackData[16:20]) - if len(ackData) - 24 != ackDataPayloadLength: - print 'ackData payload length doesn\'t match the payload length specified in the header. Not sending ackdata.' - return False - if ackData[4:16] != 'getpubkey\x00\x00\x00' and ackData[4:16] != 'pubkey\x00\x00\x00\x00\x00\x00' and ackData[4:16] != 'msg\x00\x00\x00\x00\x00\x00\x00\x00\x00' and ackData[4:16] != 'broadcast\x00\x00\x00': - return False - return True - - def addMailingListNameToSubject(self, subject, mailingListName): - subject = subject.strip() - if subject[:3] == 'Re:' or subject[:3] == 'RE:': - subject = subject[3:].strip() - if '[' + mailingListName + ']' in subject: - return subject - else: - return '[' + mailingListName + '] ' + subject - - # We have inserted a pubkey into our pubkey table which we received from a - # pubkey, msg, or broadcast message. It might be one that we have been - # waiting for. Let's check. - def possibleNewPubkey(self, ripe=None, address=None): - # For address versions <= 3, we wait on a key with the correct ripe hash - if ripe != None: - if ripe in shared.neededPubkeys: - print 'We have been awaiting the arrival of this pubkey.' - del shared.neededPubkeys[ripe] - sqlExecute( - '''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND (status='awaitingpubkey' or status='doingpubkeypow') and folder='sent' ''', - ripe) - shared.workerQueue.put(('sendmessage', '')) - else: - with shared.printLock: - print 'We don\'t need this pub key. We didn\'t ask for it. Pubkey hash:', ripe.encode('hex') - # For address versions >= 4, we wait on a pubkey with the correct tag. - # Let us create the tag from the address and see if we were waiting - # for it. - elif address != None: - status, addressVersion, streamNumber, ripe = decodeAddress(address) - tag = hashlib.sha512(hashlib.sha512(encodeVarint( - addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest()[32:] - if tag in shared.neededPubkeys: - print 'We have been awaiting the arrival of this pubkey.' - del shared.neededPubkeys[tag] - sqlExecute( - '''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND (status='awaitingpubkey' or status='doingpubkeypow') and folder='sent' ''', - ripe) - shared.workerQueue.put(('sendmessage', '')) # We have received a pubkey def recpubkey(self, data): self.pubkeyProcessingStartTime = time.time() - if len(data) < 146 or len(data) > 420: # sanity check - return - # We must check to make sure the proof of work is sufficient. - if not self.isProofOfWorkSufficient(data): - print 'Proof of work in pubkey message insufficient.' - return - readPosition = 8 # for the nonce - embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) - - # This section is used for the transition from 32 bit time to 64 bit - # time in the protocol. - if embeddedTime == 0: - embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) - readPosition += 8 - else: - readPosition += 4 - - if embeddedTime < int(time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys: - with shared.printLock: - print 'The embedded time in this pubkey message is too old. Ignoring. Embedded time is:', embeddedTime - - return - if embeddedTime > int(time.time()) + 10800: - with shared.printLock: - print 'The embedded time in this pubkey message more than several hours in the future. This is irrational. Ignoring message.' - - return - addressVersion, varintLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += varintLength - streamNumber, varintLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += varintLength - if self.streamNumber != streamNumber: - print 'stream number embedded in this pubkey doesn\'t match our stream number. Ignoring.' - return - if addressVersion >= 4: - tag = data[readPosition:readPosition + 32] - print 'tag in received pubkey is:', tag.encode('hex') - else: - tag = '' - - shared.numberOfInventoryLookupsPerformed += 1 - inventoryHash = calculateInventoryHash(data) - shared.inventoryLock.acquire() - if inventoryHash in shared.inventory: - print 'We have already received this pubkey. Ignoring it.' - shared.inventoryLock.release() - return - elif shared.isInSqlInventory(inventoryHash): - print 'We have already received this pubkey (it is stored on disk in the SQL inventory). Ignoring it.' - shared.inventoryLock.release() - return - objectType = 'pubkey' - shared.inventory[inventoryHash] = ( - objectType, self.streamNumber, data, embeddedTime, tag) - shared.inventorySets[self.streamNumber].add(inventoryHash) - shared.inventoryLock.release() - self.broadcastinv(inventoryHash) - shared.numberOfPubkeysProcessed += 1 - shared.UISignalQueue.put(( - 'updateNumberOfPubkeysProcessed', 'no data')) - - self.processpubkey(data) + shared.checkAndSharePubkeyWithPeers(data) lengthOfTimeWeShouldUseToProcessThisMessage = .1 sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \ @@ -1352,343 +385,7 @@ class receiveDataThread(threading.Thread): if sleepTime > 0 and doTimingAttackMitigation: with shared.printLock: print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.' - time.sleep(sleepTime) - with shared.printLock: - print 'Total pubkey processing time:', time.time() - self.pubkeyProcessingStartTime, 'seconds.' - - - def processpubkey(self, data): - readPosition = 8 # for the nonce - embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) - - # This section is used for the transition from 32 bit time to 64 bit - # time in the protocol. - if embeddedTime == 0: - embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) - readPosition += 8 - else: - readPosition += 4 - - addressVersion, varintLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += varintLength - streamNumber, varintLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += varintLength - if addressVersion == 0: - print '(Within processpubkey) addressVersion of 0 doesn\'t make sense.' - return - if addressVersion > 4 or addressVersion == 1: - with shared.printLock: - print 'This version of Bitmessage cannot handle version', addressVersion, 'addresses.' - - return - if addressVersion == 2: - if len(data) < 146: # sanity check. This is the minimum possible length. - print '(within processpubkey) payloadLength less than 146. Sanity check failed.' - return - bitfieldBehaviors = data[readPosition:readPosition + 4] - readPosition += 4 - publicSigningKey = data[readPosition:readPosition + 64] - # Is it possible for a public key to be invalid such that trying to - # encrypt or sign with it will cause an error? If it is, we should - # probably test these keys here. - readPosition += 64 - publicEncryptionKey = data[readPosition:readPosition + 64] - if len(publicEncryptionKey) < 64: - print 'publicEncryptionKey length less than 64. Sanity check failed.' - return - sha = hashlib.new('sha512') - sha.update( - '\x04' + publicSigningKey + '\x04' + publicEncryptionKey) - ripeHasher = hashlib.new('ripemd160') - ripeHasher.update(sha.digest()) - ripe = ripeHasher.digest() - - with shared.printLock: - print 'within recpubkey, addressVersion:', addressVersion, ', streamNumber:', streamNumber - print 'ripe', ripe.encode('hex') - print 'publicSigningKey in hex:', publicSigningKey.encode('hex') - print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex') - - - queryreturn = sqlQuery( - '''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion) - if queryreturn != []: # if this pubkey is already in our database and if we have used it personally: - print 'We HAVE used this pubkey personally. Updating time.' - t = (ripe, addressVersion, data, embeddedTime, 'yes') - else: - print 'We have NOT used this pubkey personally. Inserting in database.' - t = (ripe, addressVersion, data, embeddedTime, 'no') - # This will also update the embeddedTime. - sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) - # shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) - self.possibleNewPubkey(ripe = ripe) - if addressVersion == 3: - if len(data) < 170: # sanity check. - print '(within processpubkey) payloadLength less than 170. Sanity check failed.' - return - bitfieldBehaviors = data[readPosition:readPosition + 4] - readPosition += 4 - publicSigningKey = '\x04' + data[readPosition:readPosition + 64] - # Is it possible for a public key to be invalid such that trying to - # encrypt or sign with it will cause an error? If it is, we should - # probably test these keys here. - readPosition += 64 - publicEncryptionKey = '\x04' + data[readPosition:readPosition + 64] - readPosition += 64 - specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += specifiedNonceTrialsPerByteLength - specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += specifiedPayloadLengthExtraBytesLength - endOfSignedDataPosition = readPosition - signatureLength, signatureLengthLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += signatureLengthLength - signature = data[readPosition:readPosition + signatureLength] - try: - if not highlevelcrypto.verify(data[8:endOfSignedDataPosition], signature, publicSigningKey.encode('hex')): - print 'ECDSA verify failed (within processpubkey)' - return - print 'ECDSA verify passed (within processpubkey)' - except Exception as err: - print 'ECDSA verify failed (within processpubkey)', err - return - - sha = hashlib.new('sha512') - sha.update(publicSigningKey + publicEncryptionKey) - ripeHasher = hashlib.new('ripemd160') - ripeHasher.update(sha.digest()) - ripe = ripeHasher.digest() - - with shared.printLock: - print 'within recpubkey, addressVersion:', addressVersion, ', streamNumber:', streamNumber - print 'ripe', ripe.encode('hex') - print 'publicSigningKey in hex:', publicSigningKey.encode('hex') - print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex') - - - queryreturn = sqlQuery('''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion) - if queryreturn != []: # if this pubkey is already in our database and if we have used it personally: - print 'We HAVE used this pubkey personally. Updating time.' - t = (ripe, addressVersion, data, embeddedTime, 'yes') - else: - print 'We have NOT used this pubkey personally. Inserting in database.' - t = (ripe, addressVersion, data, embeddedTime, 'no') - # This will also update the embeddedTime. - sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) - self.possibleNewPubkey(ripe = ripe) - - if addressVersion == 4: - if len(data) < 350: # sanity check. - print '(within processpubkey) payloadLength less than 350. Sanity check failed.' - return - signedData = data[8:readPosition] # Used only for v4 or higher pubkeys - tag = data[readPosition:readPosition + 32] - readPosition += 32 - encryptedData = data[readPosition:] - if tag not in shared.neededPubkeys: - with shared.printLock: - print 'We don\'t need this v4 pubkey. We didn\'t ask for it.' - return - - # Let us try to decrypt the pubkey - cryptorObject = shared.neededPubkeys[tag] - try: - decryptedData = cryptorObject.decrypt(encryptedData) - except: - # Someone must have encrypted some data with a different key - # but tagged it with a tag for which we are watching. - with shared.printLock: - print 'Pubkey decryption was unsuccessful.' - return - - - readPosition = 0 - bitfieldBehaviors = decryptedData[readPosition:readPosition + 4] - readPosition += 4 - publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64] - # Is it possible for a public key to be invalid such that trying to - # encrypt or sign with it will cause an error? If it is, we should - # probably test these keys here. - readPosition += 64 - publicEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64] - readPosition += 64 - specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += specifiedNonceTrialsPerByteLength - specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += specifiedPayloadLengthExtraBytesLength - signedData += decryptedData[:readPosition] - signatureLength, signatureLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += signatureLengthLength - signature = decryptedData[readPosition:readPosition + signatureLength] - try: - if not highlevelcrypto.verify(signedData, signature, publicSigningKey.encode('hex')): - print 'ECDSA verify failed (within processpubkey)' - return - print 'ECDSA verify passed (within processpubkey)' - except Exception as err: - print 'ECDSA verify failed (within processpubkey)', err - return - - sha = hashlib.new('sha512') - sha.update(publicSigningKey + publicEncryptionKey) - ripeHasher = hashlib.new('ripemd160') - ripeHasher.update(sha.digest()) - ripe = ripeHasher.digest() - - # We need to make sure that the tag on the outside of the encryption - # is the one generated from hashing these particular keys. - if tag != hashlib.sha512(hashlib.sha512(encodeVarint(addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest()[32:]: - with shared.printLock: - print 'Someone was trying to act malicious: tag doesn\'t match the keys in this pubkey message. Ignoring it.' - return - else: - print 'Tag successfully matches keys in pubkey message' # testing. Will remove soon. - - with shared.printLock: - print 'within recpubkey, addressVersion:', addressVersion, ', streamNumber:', streamNumber - print 'ripe', ripe.encode('hex') - print 'publicSigningKey in hex:', publicSigningKey.encode('hex') - print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex') - - t = (ripe, addressVersion, signedData, embeddedTime, 'yes') - sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) - - fromAddress = encodeAddress(addressVersion, streamNumber, ripe) - # That this point we know that we have been waiting on this pubkey. - # This function will command the workerThread to start work on - # the messages that require it. - self.possibleNewPubkey(address = fromAddress) - - - # We have received a getpubkey message - def recgetpubkey(self, data): - if not self.isProofOfWorkSufficient(data): - print 'Proof of work in getpubkey message insufficient.' - return - if len(data) < 34: - print 'getpubkey message doesn\'t contain enough data. Ignoring.' - return - readPosition = 8 # bypass the nonce - embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) - - # This section is used for the transition from 32 bit time to 64 bit - # time in the protocol. - if embeddedTime == 0: - embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) - readPosition += 8 - else: - readPosition += 4 - - if embeddedTime > int(time.time()) + 10800: - print 'The time in this getpubkey message is too new. Ignoring it. Time:', embeddedTime - return - if embeddedTime < int(time.time()) - shared.maximumAgeOfAnObjectThatIAmWillingToAccept: - print 'The time in this getpubkey message is too old. Ignoring it. Time:', embeddedTime - return - requestedAddressVersionNumber, addressVersionLength = decodeVarint( - data[readPosition:readPosition + 10]) - readPosition += addressVersionLength - streamNumber, streamNumberLength = decodeVarint( - data[readPosition:readPosition + 10]) - if streamNumber != self.streamNumber: - print 'The streamNumber', streamNumber, 'doesn\'t match our stream number:', self.streamNumber - return - readPosition += streamNumberLength - - shared.numberOfInventoryLookupsPerformed += 1 - inventoryHash = calculateInventoryHash(data) - shared.inventoryLock.acquire() - if inventoryHash in shared.inventory: - print 'We have already received this getpubkey request. Ignoring it.' - shared.inventoryLock.release() - return - elif shared.isInSqlInventory(inventoryHash): - print 'We have already received this getpubkey request (it is stored on disk in the SQL inventory). Ignoring it.' - shared.inventoryLock.release() - return - - objectType = 'getpubkey' - shared.inventory[inventoryHash] = ( - objectType, self.streamNumber, data, embeddedTime,'') - shared.inventorySets[self.streamNumber].add(inventoryHash) - shared.inventoryLock.release() - # This getpubkey request is valid so far. Forward to peers. - self.broadcastinv(inventoryHash) - - if requestedAddressVersionNumber == 0: - print 'The requestedAddressVersionNumber of the pubkey request is zero. That doesn\'t make any sense. Ignoring it.' - return - elif requestedAddressVersionNumber == 1: - print 'The requestedAddressVersionNumber of the pubkey request is 1 which isn\'t supported anymore. Ignoring it.' - return - elif requestedAddressVersionNumber > 4: - print 'The requestedAddressVersionNumber of the pubkey request is too high. Can\'t understand. Ignoring it.' - return - - myAddress = '' - if requestedAddressVersionNumber <= 3 : - requestedHash = data[readPosition:readPosition + 20] - if len(requestedHash) != 20: - print 'The length of the requested hash is not 20 bytes. Something is wrong. Ignoring.' - return - with shared.printLock: - print 'the hash requested in this getpubkey request is:', requestedHash.encode('hex') - if requestedHash in shared.myAddressesByHash: # if this address hash is one of mine - myAddress = shared.myAddressesByHash[requestedHash] - elif requestedAddressVersionNumber >= 4: - requestedTag = data[readPosition:readPosition + 32] - if len(requestedTag) != 32: - print 'The length of the requested tag is not 32 bytes. Something is wrong. Ignoring.' - return - with shared.printLock: - print 'the tag requested in this getpubkey request is:', requestedTag.encode('hex') - if requestedTag in shared.myAddressesByTag: - - myAddress = shared.myAddressesByTag[requestedTag] - - if myAddress == '': - with shared.printLock: - print 'This getpubkey request is not for any of my keys.' - return - - if decodeAddress(myAddress)[1] != requestedAddressVersionNumber: - with shared.printLock: - sys.stderr.write( - '(Within the recgetpubkey function) Someone requested one of my pubkeys but the requestedAddressVersionNumber doesn\'t match my actual address version number. They shouldn\'t have done that. Ignoring.\n') - return - if shared.safeConfigGetBoolean(myAddress, 'chan'): - with shared.printLock: - print 'Ignoring getpubkey request because it is for one of my chan addresses. The other party should already have the pubkey.' - return - try: - lastPubkeySendTime = int(shared.config.get( - myAddress, 'lastpubkeysendtime')) - except: - lastPubkeySendTime = 0 - if lastPubkeySendTime > time.time() - shared.lengthOfTimeToHoldOnToAllPubkeys: # If the last time we sent our pubkey was more recent than 28 days ago... - with shared.printLock: - print 'Found getpubkey-requested-item in my list of EC hashes BUT we already sent it recently. Ignoring request. The lastPubkeySendTime is:', lastPubkeySendTime - return - - with shared.printLock: - print 'Found getpubkey-requested-hash in my list of EC hashes. Telling Worker thread to do the POW for a pubkey message and send it out.' - if requestedAddressVersionNumber == 2: - shared.workerQueue.put(( - 'doPOWForMyV2Pubkey', requestedHash)) - elif requestedAddressVersionNumber == 3: - shared.workerQueue.put(( - 'sendOutOrStoreMyV3Pubkey', requestedHash)) - elif requestedAddressVersionNumber == 4: - shared.workerQueue.put(( - 'sendOutOrStoreMyV4Pubkey', myAddress)) # We have received an inv message @@ -1712,7 +409,6 @@ class receiveDataThread(threading.Thread): if totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers > 200000 and len(self.objectsThatWeHaveYetToGetFromThisPeer) > 1000: # inv flooding attack mitigation with shared.printLock: print 'We already have', totalNumberOfobjectsThatWeHaveYetToGetFromAllPeers, 'items yet to retrieve from peers and over 1000 from this node in particular. Ignoring this inv message.' - return self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware[ data[lengthOfVarint:32 + lengthOfVarint]] = 0 @@ -1757,12 +453,7 @@ class receiveDataThread(threading.Thread): headerData += pack('>L', len( payload)) # payload length. Note that we add an extra 8 for the nonce. headerData += hashlib.sha512(payload).digest()[:4] - try: - self.sock.sendall(headerData + payload) - except Exception as err: - # if not 'Bad file descriptor' in err: - with shared.printLock: - print 'sock.sendall error:', err + self.sendDataThreadQueue.put((0, 'sendRawData', headerData + payload)) # We have received a getdata request from our peer @@ -1826,21 +517,16 @@ class receiveDataThread(threading.Thread): return headerData += pack('>L', len(payload)) # payload length. headerData += hashlib.sha512(payload).digest()[:4] - try: - self.sock.sendall(headerData + payload) - except Exception as err: - # if not 'Bad file descriptor' in err: - with shared.printLock: - print 'sock.sendall error:', err + self.sendDataThreadQueue.put((0, 'sendRawData', headerData + payload)) # Advertise this object to all of our peers - def broadcastinv(self, hash): + """def broadcastinv(self, hash): with shared.printLock: print 'broadcasting inv with hash:', hash.encode('hex') shared.broadcastToSendDataQueues((self.streamNumber, 'advertiseobject', hash)) - + """ # We have received an addr message. def recaddr(self, data): #listOfAddressDetailsToBroadcastToPeers = [] @@ -1863,13 +549,11 @@ class receiveDataThread(threading.Thread): if data[20 + lengthOfNumberOfAddresses + (38 * i):32 + lengthOfNumberOfAddresses + (38 * i)] != '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': with shared.printLock: print 'Skipping IPv6 address.', repr(data[20 + lengthOfNumberOfAddresses + (38 * i):32 + lengthOfNumberOfAddresses + (38 * i)]) - continue except Exception as err: with shared.printLock: sys.stderr.write( 'ERROR TRYING TO UNPACK recaddr (to test for an IPv6 address). Message: %s\n' % str(err)) - break # giving up on unpacking any more. We should still be connected however. try: @@ -1879,7 +563,6 @@ class receiveDataThread(threading.Thread): with shared.printLock: sys.stderr.write( 'ERROR TRYING TO UNPACK recaddr (recaddrStream). Message: %s\n' % str(err)) - break # giving up on unpacking any more. We should still be connected however. if recaddrStream == 0: continue @@ -1892,7 +575,6 @@ class receiveDataThread(threading.Thread): with shared.printLock: sys.stderr.write( 'ERROR TRYING TO UNPACK recaddr (recaddrServices). Message: %s\n' % str(err)) - break # giving up on unpacking any more. We should still be connected however. try: @@ -1902,7 +584,6 @@ class receiveDataThread(threading.Thread): with shared.printLock: sys.stderr.write( 'ERROR TRYING TO UNPACK recaddr (recaddrPort). Message: %s\n' % str(err)) - break # giving up on unpacking any more. We should still be connected however. # print 'Within recaddr(): IP', recaddrIP, ', Port', # recaddrPort, ', i', i @@ -1955,37 +636,6 @@ class receiveDataThread(threading.Thread): print 'knownNodes currently has', len(shared.knownNodes[self.streamNumber]), 'nodes for this stream.' - # Function runs when we want to broadcast an addr message to all of our - # peers. Runs when we learn of nodes that we didn't previously know about - # and want to share them with our peers. - """def broadcastaddr(self, listOfAddressDetailsToBroadcastToPeers): - numberOfAddressesInAddrMessage = len( - listOfAddressDetailsToBroadcastToPeers) - payload = '' - for hostDetails in listOfAddressDetailsToBroadcastToPeers: - 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 += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + \ - socket.inet_aton(host) - payload += pack('>H', port) # remote port - - payload = encodeVarint(numberOfAddressesInAddrMessage) + payload - datatosend = '\xE9\xBE\xB4\xD9addr\x00\x00\x00\x00\x00\x00\x00\x00' - datatosend = datatosend + pack('>L', len(payload)) # payload length - datatosend = datatosend + hashlib.sha512(payload).digest()[0:4] - datatosend = datatosend + payload - - if shared.verbose >= 1: - with shared.printLock: - print 'Broadcasting addr with', numberOfAddressesInAddrMessage, 'entries.' - - shared.broadcastToSendDataQueues(( - self.streamNumber, 'sendaddr', datatosend))""" - # Send a big addr message to our peer def sendaddr(self): addrsInMyStream = {} @@ -2066,16 +716,7 @@ class receiveDataThread(threading.Thread): datatosend = datatosend + pack('>L', len(payload)) # payload length datatosend = datatosend + hashlib.sha512(payload).digest()[0:4] datatosend = datatosend + payload - try: - self.sock.sendall(datatosend) - if shared.verbose >= 1: - with shared.printLock: - print 'Sending addr with', numberOfAddressesInAddrMessage, 'entries.' - - except Exception as err: - # if not 'Bad file descriptor' in err: - with shared.printLock: - print 'sock.sendall error:', err + self.sendDataThreadQueue.put((0, 'sendRawData', datatosend)) # We have received a version message @@ -2089,7 +730,6 @@ class receiveDataThread(threading.Thread): shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) with shared.printLock: print 'Closing connection to old protocol version 1 node: ', self.peer - return # print 'remoteProtocolVersion', self.remoteProtocolVersion self.myExternalIP = socket.inet_ntoa(data[40:44]) @@ -2113,7 +753,6 @@ class receiveDataThread(threading.Thread): shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) with shared.printLock: print 'Closed connection to', self.peer, 'because they are interested in stream', self.streamNumber, '.' - return shared.connectedHostsList[ self.peer.host] = 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. @@ -2126,10 +765,8 @@ class receiveDataThread(threading.Thread): shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) with shared.printLock: print 'Closing connection to myself: ', self.peer - return - shared.broadcastToSendDataQueues((0, 'setRemoteProtocolVersion', ( - self.peer, self.remoteProtocolVersion))) + self.sendDataThreadQueue.put((0, 'setRemoteProtocolVersion', self.remoteProtocolVersion)) shared.knownNodesLock.acquire() shared.knownNodes[self.streamNumber][shared.Peer(self.peer.host, self.remoteNodeIncomingPort)] = int(time.time()) @@ -2144,33 +781,15 @@ class receiveDataThread(threading.Thread): def sendversion(self): with shared.printLock: print 'Sending version message' - - try: - self.sock.sendall(shared.assembleVersionMessage( - self.peer.host, self.peer.port, self.streamNumber)) - except Exception as err: - # if not 'Bad file descriptor' in err: - with shared.printLock: - print 'sock.sendall error:', err + self.sendDataThreadQueue.put((0, 'sendRawData', shared.assembleVersionMessage( + self.peer.host, self.peer.port, self.streamNumber))) # Sends a verack message def sendverack(self): with shared.printLock: print 'Sending verack' - - try: - self.sock.sendall( - '\xE9\xBE\xB4\xD9\x76\x65\x72\x61\x63\x6B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcf\x83\xe1\x35') - except Exception as err: - # if not 'Bad file descriptor' in err: - with shared.printLock: - print 'sock.sendall error:', err - - # cf - # 83 - # e1 - # 35 + self.sendDataThreadQueue.put((0, 'sendRawData', '\xE9\xBE\xB4\xD9\x76\x65\x72\x61\x63\x6B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcf\x83\xe1\x35')) self.verackSent = True if self.verackReceived: self.connectionFullyEstablished() diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index 240f9c64..14feb637 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -15,15 +15,15 @@ from addresses import * # receiveDataThread). class sendDataThread(threading.Thread): - def __init__(self): + def __init__(self, sendDataThreadQueue): threading.Thread.__init__(self) - self.mailbox = Queue.Queue() - shared.sendDataQueues.append(self.mailbox) + self.sendDataThreadQueue = sendDataThreadQueue + shared.sendDataQueues.append(self.sendDataThreadQueue) with shared.printLock: print 'The length of sendDataQueues at sendDataThread init is:', len(shared.sendDataQueues) self.data = '' - self.objectHashHolderInstance = objectHashHolder(self.mailbox) + self.objectHashHolderInstance = objectHashHolder(self.sendDataThreadQueue) self.objectHashHolderInstance.start() @@ -38,7 +38,7 @@ class sendDataThread(threading.Thread): self.peer = shared.Peer(HOST, PORT) self.streamNumber = streamNumber self.remoteProtocolVersion = - \ - 1 # This must be set using setRemoteProtocolVersion command which is sent through the self.mailbox queue. + 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. self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware = someObjectsOfWhichThisRemoteNodeIsAlreadyAware @@ -64,7 +64,7 @@ class sendDataThread(threading.Thread): def run(self): while True: - deststream, command, data = self.mailbox.get() + deststream, command, data = self.sendDataThreadQueue.get() if deststream == self.streamNumber or deststream == 0: if command == 'shutdown': @@ -77,7 +77,7 @@ class sendDataThread(threading.Thread): self.sock.close() except: pass - shared.sendDataQueues.remove(self.mailbox) + shared.sendDataQueues.remove(self.sendDataThreadQueue) with shared.printLock: print 'len of sendDataQueues', len(shared.sendDataQueues) @@ -96,12 +96,10 @@ class sendDataThread(threading.Thread): self.streamNumber = specifiedStreamNumber elif command == 'setRemoteProtocolVersion': - peerInMessage, specifiedRemoteProtocolVersion = data - if peerInMessage == self.peer: - with shared.printLock: - print 'setting the remote node\'s protocol version in the sendData thread (ID:', id(self), ') to', specifiedRemoteProtocolVersion - - self.remoteProtocolVersion = specifiedRemoteProtocolVersion + specifiedRemoteProtocolVersion = data + with shared.printLock: + print 'setting the remote node\'s protocol version in the sendDataThread (ID:', id(self), ') to', specifiedRemoteProtocolVersion + self.remoteProtocolVersion = specifiedRemoteProtocolVersion elif command == 'advertisepeer': self.objectHashHolderInstance.holdPeer(data) elif command == 'sendaddr': @@ -135,7 +133,7 @@ class sendDataThread(threading.Thread): self.sock.close() except: pass - shared.sendDataQueues.remove(self.mailbox) + shared.sendDataQueues.remove(self.sendDataThreadQueue) print 'sendDataThread thread (ID:', str(id(self)) + ') ending now. Was connected to', self.peer break elif command == 'advertiseobject': @@ -161,7 +159,7 @@ class sendDataThread(threading.Thread): self.sock.close() except: pass - shared.sendDataQueues.remove(self.mailbox) + shared.sendDataQueues.remove(self.sendDataThreadQueue) print 'sendDataThread thread (ID:', str(id(self)) + ') ending now. Was connected to', self.peer break elif command == 'pong': @@ -182,9 +180,22 @@ class sendDataThread(threading.Thread): self.sock.close() except: pass - shared.sendDataQueues.remove(self.mailbox) + shared.sendDataQueues.remove(self.sendDataThreadQueue) print 'sendDataThread thread', self, 'ending now. Was connected to', self.peer break + elif command == 'sendRawData': + try: + self.sock.sendall(data) + self.lastTimeISentData = int(time.time()) + except: + try: + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + except: + pass + shared.sendDataQueues.remove(self.sendDataThreadQueue) + print 'Sending of data to', self.peer, 'failed. sendDataThread thread', self, 'ending now.' + break else: with shared.printLock: print 'sendDataThread ID:', id(self), 'ignoring command', command, 'because the thread is not in stream', deststream diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index ca52b8e9..74ecedac 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -2,6 +2,7 @@ import threading import shared import time import sys +import os import pickle import tr#anslate diff --git a/src/class_singleListener.py b/src/class_singleListener.py index 3890447a..ec1f47d4 100644 --- a/src/class_singleListener.py +++ b/src/class_singleListener.py @@ -69,9 +69,10 @@ class singleListener(threading.Thread): a.close() a, (HOST, PORT) = sock.accept() someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory. + sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection. a.settimeout(20) - sd = sendDataThread() + sd = sendDataThread(sendDataThreadQueue) sd.setup( a, HOST, PORT, -1, someObjectsOfWhichThisRemoteNodeIsAlreadyAware) sd.start() @@ -79,7 +80,7 @@ class singleListener(threading.Thread): rd = receiveDataThread() rd.daemon = True # close the main program even if there are threads left rd.setup( - a, HOST, PORT, -1, someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections) + a, HOST, PORT, -1, someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections, sendDataThreadQueue) rd.start() with shared.printLock: diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 10ba241c..4b43c22f 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -260,7 +260,6 @@ class singleWorker(threading.Thread): payload = pack('>Q', (embeddedTime)) payload += encodeVarint(addressVersionNumber) # Address version number payload += encodeVarint(streamNumber) - dataToStoreInOurPubkeysTable = payload # used if this is a chan. We'll add more data further down. dataToEncrypt = '\x00\x00\x00\x01' # bitfield of features supported by me (see the wiki). @@ -291,8 +290,6 @@ class singleWorker(threading.Thread): dataToEncrypt += encodeVarint(shared.config.getint( myAddress, 'payloadlengthextrabytes')) - dataToStoreInOurPubkeysTable += dataToEncrypt # dataToStoreInOurPubkeysTable is used if this is a chan - signature = highlevelcrypto.sign(payload + dataToEncrypt, privSigningKeyHex) dataToEncrypt += encodeVarint(len(signature)) dataToEncrypt += signature @@ -337,10 +334,8 @@ class singleWorker(threading.Thread): myAddress, 'lastpubkeysendtime', str(int(time.time()))) with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) - except: - # The user deleted the address out of the keys.dat file before this - # finished. - pass + except Exception as err: + logger.error('Error: Couldn\'t add the lastpubkeysendtime to the keys.dat file. Error message: %s' % err) def sendBroadcast(self): queryreturn = sqlQuery( @@ -386,7 +381,10 @@ class singleWorker(threading.Thread): if addressVersionNumber >= 4: doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( addressVersionNumber) + encodeVarint(streamNumber) + ripe).digest()).digest() - payload += doubleHashOfAddressData[32:] # the tag + tag = doubleHashOfAddressData[32:] + payload += tag + else: + tag = '' if addressVersionNumber <= 3: dataToEncrypt = encodeVarint(2) # broadcast version @@ -416,6 +414,7 @@ class singleWorker(threading.Thread): addressVersionNumber) + encodeVarint(streamNumber) + ripe).digest()[:32] else: privEncryptionKey = doubleHashOfAddressData[:32] + pubEncryptionKey = pointMult(privEncryptionKey) payload += highlevelcrypto.encrypt( dataToEncrypt, pubEncryptionKey.encode('hex')) @@ -434,7 +433,7 @@ class singleWorker(threading.Thread): inventoryHash = calculateInventoryHash(payload) objectType = 'broadcast' shared.inventory[inventoryHash] = ( - objectType, streamNumber, payload, int(time.time()),'') + objectType, streamNumber, payload, int(time.time()),tag) shared.inventorySets[streamNumber].add(inventoryHash) with shared.printLock: print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex') @@ -494,7 +493,7 @@ class singleWorker(threading.Thread): if queryreturn != []: # if there was a pubkey in our inventory with the correct tag, we need to try to decrypt it. for row in queryreturn: data, = row - if shared.decryptAndCheckPubkeyPayload(data[8:], toaddress) == 'successful': + if shared.decryptAndCheckPubkeyPayload(data, toaddress) == 'successful': needToRequestPubkey = False print 'debug. successfully decrypted and checked pubkey from sql inventory.' #testing sqlExecute( @@ -508,7 +507,7 @@ class singleWorker(threading.Thread): for hash, storedValue in shared.inventory.items(): objectType, streamNumber, payload, receivedTime, tag = storedValue if objectType == 'pubkey' and tag == toTag: - result = shared.decryptAndCheckPubkeyPayload(payload[8:], toaddress) #if valid, this function also puts it in the pubkeys table. + result = shared.decryptAndCheckPubkeyPayload(payload, toaddress) #if valid, this function also puts it in the pubkeys table. if result == 'successful': print 'debug. successfully decrypted and checked pubkey from memory inventory.' needToRequestPubkey = False @@ -682,7 +681,7 @@ class singleWorker(threading.Thread): ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.")))) embeddedTime = pack('>Q', (int(time.time()) + random.randrange( - -300, 300))) # the current time plus or minus five minutes. We will use this time both for our message and for the ackdata packed within our message. + -300, 300))) # the current time plus or minus five minutes. if fromAddressVersionNumber == 2: payload = '\x01' # Message version. payload += encodeVarint(fromAddressVersionNumber) @@ -722,7 +721,7 @@ class singleWorker(threading.Thread): payload += encodeVarint(len(messageToTransmit)) payload += messageToTransmit fullAckPayload = self.generateFullAckMessage( - ackdata, toStreamNumber, embeddedTime) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out. + ackdata, toStreamNumber) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out. payload += encodeVarint(len(fullAckPayload)) payload += fullAckPayload signature = highlevelcrypto.sign(payload, privSigningKeyHex) @@ -791,7 +790,7 @@ class singleWorker(threading.Thread): fullAckPayload = '' else: fullAckPayload = self.generateFullAckMessage( - ackdata, toStreamNumber, embeddedTime) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out. + ackdata, toStreamNumber) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out. payload += encodeVarint(len(fullAckPayload)) payload += fullAckPayload signature = highlevelcrypto.sign(payload, privSigningKeyHex) @@ -930,7 +929,9 @@ class singleWorker(threading.Thread): shared.UISignalQueue.put(('updateSentItemStatusByHash', (ripe, tr.translateText("MainWindow",'Sending public key request. Waiting for reply. Requested at %1').arg(unicode( strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) - def generateFullAckMessage(self, ackdata, toStreamNumber, embeddedTime): + def generateFullAckMessage(self, ackdata, toStreamNumber): + embeddedTime = pack('>Q', (int(time.time()) + random.randrange( + -300, 300))) # the current time plus or minus five minutes. payload = embeddedTime + encodeVarint(toStreamNumber) + ackdata target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index ad8ee4f6..2df6c05f 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -25,6 +25,7 @@ class sqlThread(threading.Thread): self.conn = sqlite3.connect(shared.appdata + 'messages.dat') self.conn.text_factory = str self.cur = self.conn.cursor() + try: self.cur.execute( '''CREATE TABLE inbox (msgid blob, toaddress text, fromaddress text, subject text, received text, message text, folder text, encodingtype int, read bool, UNIQUE(msgid) ON CONFLICT REPLACE)''' ) @@ -51,17 +52,15 @@ class sqlThread(threading.Thread): '''CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)''' ) self.cur.execute( '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)''' ) - self.cur.execute( - '''CREATE TABLE knownnodes (timelastseen int, stream int, services blob, host blob, port blob, UNIQUE(host, stream, port) ON CONFLICT REPLACE)''' ) - # This table isn't used in the program yet but I - # have a feeling that we'll need it. self.cur.execute( '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''') self.cur.execute( '''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' ) - self.cur.execute( '''INSERT INTO settings VALUES('version','5')''') + self.cur.execute( '''INSERT INTO settings VALUES('version','6')''') self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', ( int(time.time()),)) + self.cur.execute( + '''CREATE TABLE objectprocessorqueue (objecttype text, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)''' ) self.conn.commit() logger.info('Created messages database file') except Exception as err: @@ -290,6 +289,20 @@ class sqlThread(threading.Thread): with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) + # Add a new table: objectprocessorqueue with which to hold objects + # that have yet to be processed if the user shuts down Bitmessage. + item = '''SELECT value FROM settings WHERE key='version';''' + parameters = '' + self.cur.execute(item, parameters) + currentVersion = int(self.cur.fetchall()[0][0]) + if currentVersion == 5: + self.cur.execute( '''DROP TABLE knownnodes''') + self.cur.execute( + '''CREATE TABLE objectprocessorqueue (objecttype text, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)''' ) + item = '''update settings set value=? WHERE key='version';''' + parameters = (6,) + self.cur.execute(item, parameters) + # Are you hoping to add a new option to the keys.dat file of existing # Bitmessage users? Add it right above this line! diff --git a/src/helper_startup.py b/src/helper_startup.py index 0e35e594..abc4958f 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -5,6 +5,8 @@ import os import locale import random import string +import platform +from distutils.version import StrictVersion from namecoin import ensureNamecoinOptions @@ -119,3 +121,13 @@ def loadConfig(): os.umask(0o077) with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) + +def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections(): + try: + VER_THIS=StrictVersion(platform.version()) + if sys.platform[0:3]=="win": + return StrictVersion("5.1.2600")<=VER_THIS and StrictVersion("6.0.6000")>=VER_THIS + return False + except Exception as err: + print 'An Exception occurred within isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections:', err + return False \ No newline at end of file diff --git a/src/proofofwork.py b/src/proofofwork.py index 17a13eb8..c26f2e1b 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -57,7 +57,7 @@ def _doFastPoW(target, initialHash): for i in range(pool_size): result.append(pool.apply_async(_pool_worker, args = (i, initialHash, target, pool_size))) while True: - if shared.shutdown: + if shared.shutdown >= 1: pool.terminate() while True: time.sleep(10) # Don't let this thread return here; it will return nothing and cause an exception in bitmessagemain.py diff --git a/src/shared.py b/src/shared.py index 6a1750e2..e6642c70 100644 --- a/src/shared.py +++ b/src/shared.py @@ -1,4 +1,4 @@ -softwareVersion = '0.4.1' +softwareVersion = '0.4.2' verbose = 1 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 # Equals two days and 12 hours. lengthOfTimeToLeaveObjectsInInventory = 237600 # Equals two days and 18 hours. This should be longer than maximumAgeOfAnObjectThatIAmWillingToAccept so that we don't process messages twice. @@ -45,6 +45,8 @@ sendDataQueues = [] #each sendData thread puts its queue in this list. inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet). inventoryLock = threading.Lock() #Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual) printLock = threading.Lock() +objectProcessorQueueSizeLock = threading.Lock() +objectProcessorQueueSize = 0 # in Bytes. We maintain this to prevent nodes from flooing us with objects which take up too much memory. If this gets too big we'll sleep before asking for further objects. appdata = '' #holds the location of the application data storage directory statusIconColor = 'red' connectedHostsList = {} #List of hosts to which we are connected. Used to guarantee that the outgoingSynSender threads won't connect to the same remote node twice. @@ -72,6 +74,9 @@ daemon = False inventorySets = {} # key = streamNumer, value = a set which holds the inventory object hashes that we are aware of. This is used whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it every couple hours. needToWriteKnownNodesToDisk = False # If True, the singleCleaner will write it to disk eventually. maximumLengthOfTimeToBotherResendingMessages = 0 +objectProcessorQueue = Queue.Queue( + ) # receiveDataThreads dump objects they hear on the network into this queue to be processed. +streamsInWhichIAmParticipating = {} #If changed, these values will cause particularly unexpected behavior: You won't be able to either send or receive messages because the proof of work you do (or demand) won't match that done or demanded by others. Don't change them! networkDefaultProofOfWorkNonceTrialsPerByte = 320 #The amount of work that should be performed (and demanded) per byte of the payload. Double this number to double the work. @@ -89,10 +94,7 @@ frozen = getattr(sys,'frozen', None) def isInSqlInventory(hash): queryreturn = sqlQuery('''select hash from inventory where hash=?''', hash) - if queryreturn == []: - return False - else: - return True + return queryreturn != [] def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): payload = '' @@ -101,7 +103,7 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): payload += pack('>q', int(time.time())) payload += pack( - '>q', 1) # boolservices of remote connection. How can I even know this for sure? This is probably ignored by the remote host. + '>q', 1) # boolservices of remote connection; ignored by the remote host. payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + \ socket.inet_aton(remoteHost) payload += pack('>H', remotePort) # remote IPv6 and port @@ -110,7 +112,7 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack( '>L', 2130706433) # = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used. payload += pack('>H', shared.config.getint( - 'bitmessagesettings', 'port')) # my external IPv6 and port + 'bitmessagesettings', 'port')) random.seed() payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf @@ -129,7 +131,6 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): def lookupAppdataFolder(): APPNAME = "PyBitmessage" - from os import path, environ if "BITMESSAGE_HOME" in environ: dataFolder = environ["BITMESSAGE_HOME"] if dataFolder[-1] not in [os.path.sep, os.path.altsep]: @@ -146,7 +147,7 @@ def lookupAppdataFolder(): sys.exit() elif 'win32' in sys.platform or 'win64' in sys.platform: - dataFolder = path.join(environ['APPDATA'], APPNAME) + '\\' + dataFolder = path.join(environ['APPDATA'].decode(sys.getfilesystemencoding(), 'ignore'), APPNAME) + path.sep else: from shutil import move try: @@ -207,7 +208,8 @@ def decodeWalletImportFormat(WIFstring): privkey = fullString[:-4] if fullString[-4:] != hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]: logger.critical('Major problem! When trying to decode one of your private keys, the checksum ' - 'failed. Here is the PRIVATE key: %s' % str(WIFstring)) + 'failed. Here are the first 6 characters of the PRIVATE key: %s' % str(WIFstring)[:6]) + os._exit(0) return "" else: #checksum passed @@ -217,6 +219,7 @@ def decodeWalletImportFormat(WIFstring): logger.critical('Major problem! When trying to decode one of your private keys, the ' 'checksum passed but the key doesn\'t begin with hex 80. Here is the ' 'PRIVATE key: %s' % str(WIFstring)) + os._exit(0) return "" @@ -277,10 +280,26 @@ def reloadBroadcastSendersForWhichImWatching(): privEncryptionKey = doubleHashOfAddressData[:32] MyECSubscriptionCryptorObjects[tag] = highlevelcrypto.makeCryptor(privEncryptionKey.encode('hex')) +def isProofOfWorkSufficient( + data, + nonceTrialsPerByte=0, + payloadLengthExtraBytes=0): + if nonceTrialsPerByte < networkDefaultProofOfWorkNonceTrialsPerByte: + nonceTrialsPerByte = networkDefaultProofOfWorkNonceTrialsPerByte + if payloadLengthExtraBytes < networkDefaultPayloadLengthExtraBytes: + payloadLengthExtraBytes = networkDefaultPayloadLengthExtraBytes + POW, = unpack('>Q', hashlib.sha512(hashlib.sha512(data[ + :8] + hashlib.sha512(data[8:]).digest()).digest()).digest()[0:8]) + return POW <= 2 ** 64 / ((len(data) + payloadLengthExtraBytes) * (nonceTrialsPerByte)) + def doCleanShutdown(): global shutdown - shutdown = 1 #Used to tell proof of work worker threads to exit. - broadcastToSendDataQueues((0, 'shutdown', 'all')) + shutdown = 1 #Used to tell proof of work worker threads and the objectProcessorThread to exit. + broadcastToSendDataQueues((0, 'shutdown', 'all')) + with shared.objectProcessorQueueSizeLock: + data = 'no data' + shared.objectProcessorQueueSize += len(data) + objectProcessorQueue.put(('checkShutdownVariable',data)) knownNodesLock.acquire() UISignalQueue.put(('updateStatusBar','Saving the knownNodes list of peers to disk...')) @@ -298,13 +317,18 @@ def doCleanShutdown(): 'updateStatusBar', 'Flushing inventory in memory out to disk. This should normally only take a second...')) flushInventory() - - # This one last useless query will guarantee that the previous flush committed before we close - # the program. + + # Verify that the objectProcessor has finished exiting. It should have incremented the + # shutdown variable from 1 to 2. This must finish before we command the sqlThread to exit. + while shutdown == 1: + time.sleep(.1) + + # This one last useless query will guarantee that the previous flush committed and that the + # objectProcessorThread committed before we close the program. sqlQuery('SELECT address FROM subscriptions') - sqlStoredProcedure('exit') logger.info('Finished flushing inventory.') - + sqlStoredProcedure('exit') + # Wait long enough to guarantee that any running proof of work worker threads will check the # shutdown variable and exit. If the main thread closes before they do then they won't stop. time.sleep(.25) @@ -401,23 +425,21 @@ def decryptAndCheckPubkeyPayload(payload, address): status, addressVersion, streamNumber, ripe = decodeAddress(address) doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest() - # this function expects that the nonce is Not included in payload. - readPosition = 8 # for the time + readPosition = 8 # bypass the nonce + readPosition += 8 # bypass the time embeddedVersionNumber, varintLength = decodeVarint( payload[readPosition:readPosition + 10]) if embeddedVersionNumber != addressVersion: - with shared.printLock: - print 'Pubkey decryption was UNsuccessful due to address version mismatch. This shouldn\'t have happened.' + logger.info('Pubkey decryption was UNsuccessful due to address version mismatch. This shouldn\'t have happened.') return 'failed' readPosition += varintLength embeddedStreamNumber, varintLength = decodeVarint( payload[readPosition:readPosition + 10]) if embeddedStreamNumber != streamNumber: - with shared.printLock: - print 'Pubkey decryption was UNsuccessful due to stream number mismatch. This shouldn\'t have happened.' + logger.info('Pubkey decryption was UNsuccessful due to stream number mismatch. This shouldn\'t have happened.') return 'failed' readPosition += varintLength - signedData = payload[:readPosition] # Some of the signed data is not encrypted so let's keep it for now. + signedData = payload[8:readPosition] # Some of the signed data is not encrypted so let's keep it for now. toTag = payload[readPosition:readPosition+32] readPosition += 32 #for the tag encryptedData = payload[readPosition:] @@ -429,14 +451,13 @@ def decryptAndCheckPubkeyPayload(payload, address): except: # Someone must have encrypted some data with a different key # but tagged it with a tag for which we are watching. - with shared.printLock: - print 'Pubkey decryption was UNsuccessful. This shouldn\'t have happened.' + logger.info('Pubkey decryption was UNsuccessful. This shouldn\'t have happened.') return 'failed' - print 'Pubkey decryption successful' + logger.debug('Pubkey decryption successful') readPosition = 4 # bypass the behavior bitfield publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64] # Is it possible for a public key to be invalid such that trying to - # encrypt or sign with it will cause an error? If it is, we should + # encrypt or check a sig with it will cause an error? If it is, we should # probably test these keys here. readPosition += 64 publicEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64] @@ -454,11 +475,11 @@ def decryptAndCheckPubkeyPayload(payload, address): signature = decryptedData[readPosition:readPosition + signatureLength] try: if not highlevelcrypto.verify(signedData, signature, publicSigningKey.encode('hex')): - print 'ECDSA verify failed (within decryptAndCheckPubkeyPayload).' + logger.info('ECDSA verify failed (within decryptAndCheckPubkeyPayload).') return 'failed' - print 'ECDSA verify passed (within decryptAndCheckPubkeyPayload)' + logger.debug('ECDSA verify passed (within decryptAndCheckPubkeyPayload)') except Exception as err: - print 'ECDSA verify failed (within decryptAndCheckPubkeyPayload)', err + logger.debug('ECDSA verify failed (within decryptAndCheckPubkeyPayload) %s' % err) return 'failed' sha = hashlib.new('sha512') @@ -471,8 +492,7 @@ def decryptAndCheckPubkeyPayload(payload, address): # Although this pubkey object had the tag were were looking for and was # encrypted with the correct encryption key, it doesn't contain the # correct keys. Someone is either being malicious or using buggy software. - with shared.printLock: - print 'Pubkey decryption was UNsuccessful due to RIPE mismatch. This shouldn\'t have happened.' + logger.info('Pubkey decryption was UNsuccessful due to RIPE mismatch. This shouldn\'t have happened.') return 'failed' t = (ripe, addressVersion, signedData, int(time.time()), 'yes') @@ -481,5 +501,263 @@ def decryptAndCheckPubkeyPayload(payload, address): Peer = collections.namedtuple('Peer', ['host', 'port']) +def checkAndShareMsgWithPeers(data): + # Let us check to make sure that the proof of work is sufficient. + if not isProofOfWorkSufficient(data): + logger.debug('Proof of work in msg message insufficient.') + return + + readPosition = 8 + embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) + + # This section is used for the transition from 32 bit time to 64 bit + # time in the protocol. + if embeddedTime == 0: + embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) + readPosition += 8 + else: + readPosition += 4 + + if embeddedTime > (int(time.time()) + 10800): + logger.debug('The embedded time in this msg message is more than three hours in the future. That doesn\'t make sense. Ignoring message.') + return + if embeddedTime < (int(time.time()) - maximumAgeOfAnObjectThatIAmWillingToAccept): + logger.debug('The embedded time in this msg message is too old. Ignoring message.') + return + streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = decodeVarint( + data[readPosition:readPosition + 9]) + if not streamNumberAsClaimedByMsg in streamsInWhichIAmParticipating: + logger.debug('The streamNumber %s isn\'t one we are interested in.' % streamNumberAsClaimedByMsg) + return + readPosition += streamNumberAsClaimedByMsgLength + inventoryHash = calculateInventoryHash(data) + shared.numberOfInventoryLookupsPerformed += 1 + inventoryLock.acquire() + if inventoryHash in inventory: + logger.debug('We have already received this msg message. Ignoring.') + inventoryLock.release() + return + elif isInSqlInventory(inventoryHash): + logger.debug('We have already received this msg message (it is stored on disk in the SQL inventory). Ignoring it.') + inventoryLock.release() + return + # This msg message is valid. Let's let our peers know about it. + objectType = 'msg' + inventory[inventoryHash] = ( + objectType, streamNumberAsClaimedByMsg, data, embeddedTime,'') + inventorySets[streamNumberAsClaimedByMsg].add(inventoryHash) + inventoryLock.release() + logger.debug('advertising inv with hash: %s' % inventoryHash.encode('hex')) + broadcastToSendDataQueues((streamNumberAsClaimedByMsg, 'advertiseobject', inventoryHash)) + + # Now let's enqueue it to be processed ourselves. + # If we already have too much data in the queue to be processed, just sleep for now. + while shared.objectProcessorQueueSize > 120000000: + time.sleep(2) + with shared.objectProcessorQueueSizeLock: + shared.objectProcessorQueueSize += len(data) + objectProcessorQueue.put((objectType,data)) + +def checkAndSharegetpubkeyWithPeers(data): + if not isProofOfWorkSufficient(data): + logger.debug('Proof of work in getpubkey message insufficient.') + return + if len(data) < 34: + logger.debug('getpubkey message doesn\'t contain enough data. Ignoring.') + return + readPosition = 8 # bypass the nonce + embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) + + # This section is used for the transition from 32 bit time to 64 bit + # time in the protocol. + if embeddedTime == 0: + embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) + readPosition += 8 + else: + readPosition += 4 + + if embeddedTime > int(time.time()) + 10800: + logger.debug('The time in this getpubkey message is too new. Ignoring it. Time: %s' % embeddedTime) + return + if embeddedTime < int(time.time()) - maximumAgeOfAnObjectThatIAmWillingToAccept: + logger.debug('The time in this getpubkey message is too old. Ignoring it. Time: %s' % embeddedTime) + return + requestedAddressVersionNumber, addressVersionLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += addressVersionLength + streamNumber, streamNumberLength = decodeVarint( + data[readPosition:readPosition + 10]) + if not streamNumber in streamsInWhichIAmParticipating: + logger.debug('The streamNumber %s isn\'t one we are interested in.' % streamNumber) + return + readPosition += streamNumberLength + + shared.numberOfInventoryLookupsPerformed += 1 + inventoryHash = calculateInventoryHash(data) + inventoryLock.acquire() + if inventoryHash in inventory: + logger.debug('We have already received this getpubkey request. Ignoring it.') + inventoryLock.release() + return + elif isInSqlInventory(inventoryHash): + logger.debug('We have already received this getpubkey request (it is stored on disk in the SQL inventory). Ignoring it.') + inventoryLock.release() + return + + objectType = 'getpubkey' + inventory[inventoryHash] = ( + objectType, streamNumber, data, embeddedTime,'') + inventorySets[streamNumber].add(inventoryHash) + inventoryLock.release() + # This getpubkey request is valid. Forward to peers. + logger.debug('advertising inv with hash: %s' % inventoryHash.encode('hex')) + broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) + + # Now let's queue it to be processed ourselves. + # If we already have too much data in the queue to be processed, just sleep for now. + while shared.objectProcessorQueueSize > 120000000: + time.sleep(2) + with shared.objectProcessorQueueSizeLock: + shared.objectProcessorQueueSize += len(data) + objectProcessorQueue.put((objectType,data)) + +def checkAndSharePubkeyWithPeers(data): + if len(data) < 146 or len(data) > 420: # sanity check + return + # Let us make sure that the proof of work is sufficient. + if not isProofOfWorkSufficient(data): + logger.debug('Proof of work in pubkey message insufficient.') + return + + readPosition = 8 # for the nonce + embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) + + # This section is used for the transition from 32 bit time to 64 bit + # time in the protocol. + if embeddedTime == 0: + embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) + readPosition += 8 + else: + readPosition += 4 + + if embeddedTime < int(time.time()) - lengthOfTimeToHoldOnToAllPubkeys: + logger.debug('The embedded time in this pubkey message is too old. Ignoring. Embedded time is: %s' % embeddedTime) + return + if embeddedTime > int(time.time()) + 10800: + logger.debug('The embedded time in this pubkey message more than several hours in the future. This is irrational. Ignoring message.') + return + addressVersion, varintLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += varintLength + streamNumber, varintLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += varintLength + if not streamNumber in streamsInWhichIAmParticipating: + logger.debug('The streamNumber %s isn\'t one we are interested in.' % streamNumber) + return + if addressVersion >= 4: + tag = data[readPosition:readPosition + 32] + logger.debug('tag in received pubkey is: %s' % tag.encode('hex')) + else: + tag = '' + + shared.numberOfInventoryLookupsPerformed += 1 + inventoryHash = calculateInventoryHash(data) + inventoryLock.acquire() + if inventoryHash in inventory: + logger.debug('We have already received this pubkey. Ignoring it.') + inventoryLock.release() + return + elif isInSqlInventory(inventoryHash): + logger.debug('We have already received this pubkey (it is stored on disk in the SQL inventory). Ignoring it.') + inventoryLock.release() + return + objectType = 'pubkey' + inventory[inventoryHash] = ( + objectType, streamNumber, data, embeddedTime, tag) + inventorySets[streamNumber].add(inventoryHash) + inventoryLock.release() + # This object is valid. Forward it to peers. + logger.debug('advertising inv with hash: %s' % inventoryHash.encode('hex')) + broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) + + + # Now let's queue it to be processed ourselves. + # If we already have too much data in the queue to be processed, just sleep for now. + while shared.objectProcessorQueueSize > 120000000: + time.sleep(2) + with shared.objectProcessorQueueSizeLock: + shared.objectProcessorQueueSize += len(data) + objectProcessorQueue.put((objectType,data)) + + +def checkAndShareBroadcastWithPeers(data): + # Let us verify that the proof of work is sufficient. + if not isProofOfWorkSufficient(data): + logger.debug('Proof of work in broadcast message insufficient.') + return + readPosition = 8 # bypass the nonce + embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) + + # This section is used for the transition from 32 bit time to 64 bit + # time in the protocol. + if embeddedTime == 0: + embeddedTime, = unpack('>Q', data[readPosition:readPosition + 8]) + readPosition += 8 + else: + readPosition += 4 + + if embeddedTime > (int(time.time()) + 10800): # prevent funny business + logger.debug('The embedded time in this broadcast message is more than three hours in the future. That doesn\'t make sense. Ignoring message.') + return + if embeddedTime < (int(time.time()) - maximumAgeOfAnObjectThatIAmWillingToAccept): + logger.debug('The embedded time in this broadcast message is too old. Ignoring message.') + return + if len(data) < 180: + logger.debug('The payload length of this broadcast packet is unreasonably low. Someone is probably trying funny business. Ignoring message.') + return + broadcastVersion, broadcastVersionLength = decodeVarint( + data[readPosition:readPosition + 10]) + readPosition += broadcastVersionLength + if broadcastVersion >= 2: + streamNumber, streamNumberLength = decodeVarint(data[readPosition:readPosition + 10]) + readPosition += streamNumberLength + if not streamNumber in streamsInWhichIAmParticipating: + logger.debug('The streamNumber %s isn\'t one we are interested in.' % streamNumber) + return + if broadcastVersion >= 3: + tag = data[readPosition:readPosition+32] + else: + tag = '' + shared.numberOfInventoryLookupsPerformed += 1 + inventoryLock.acquire() + inventoryHash = calculateInventoryHash(data) + if inventoryHash in inventory: + logger.debug('We have already received this broadcast object. Ignoring.') + inventoryLock.release() + return + elif isInSqlInventory(inventoryHash): + logger.debug('We have already received this broadcast object (it is stored on disk in the SQL inventory). Ignoring it.') + inventoryLock.release() + return + # It is valid. Let's let our peers know about it. + objectType = 'broadcast' + inventory[inventoryHash] = ( + objectType, streamNumber, data, embeddedTime, tag) + inventorySets[streamNumber].add(inventoryHash) + inventoryLock.release() + # This object is valid. Forward it to peers. + logger.debug('advertising inv with hash: %s' % inventoryHash.encode('hex')) + broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) + + # Now let's queue it to be processed ourselves. + # If we already have too much data in the queue to be processed, just sleep for now. + while shared.objectProcessorQueueSize > 120000000: + time.sleep(2) + with shared.objectProcessorQueueSizeLock: + shared.objectProcessorQueueSize += len(data) + objectProcessorQueue.put((objectType,data)) + + helper_startup.loadConfig() from debug import logger diff --git a/src/translations/bitmessage_ar.pro b/src/translations/bitmessage_ar.pro new file mode 100644 index 00000000..1cd8dd64 --- /dev/null +++ b/src/translations/bitmessage_ar.pro @@ -0,0 +1,35 @@ +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\ + ../helper_bootstrap.py\ + ../helper_generic.py\ + ../helper_inbox.py\ + ../helper_sent.py\ + ../helper_startup.py\ + ../shared.py\ + ../bitmessageqt/__init__.py\ + ../bitmessageqt/about.py\ + ../bitmessageqt/addaddressdialog.py\ + ../bitmessageqt/bitmessageui.py\ + ../bitmessageqt/connect.py\ + ../bitmessageqt/help.py\ + ../bitmessageqt/iconglossary.py\ + ../bitmessageqt/newaddressdialog.py\ + ../bitmessageqt/newchandialog.py\ + ../bitmessageqt/newsubscriptiondialog.py\ + ../bitmessageqt/regenerateaddresses.py\ + ../bitmessageqt/settings.py\ + ../bitmessageqt/specialaddressbehavior.py + + +TRANSLATIONS = bitmessage_ar.ts +CODECFORTR = UTF-8 diff --git a/src/translations/bitmessage_ar.qm b/src/translations/bitmessage_ar.qm new file mode 100644 index 00000000..477a7586 Binary files /dev/null and b/src/translations/bitmessage_ar.qm differ diff --git a/src/translations/bitmessage_ar.ts b/src/translations/bitmessage_ar.ts new file mode 100644 index 00000000..b37dcc0b --- /dev/null +++ b/src/translations/bitmessage_ar.ts @@ -0,0 +1,1680 @@ + + + + + MainWindow + + + Bitmessage + Bitmessage + + + + To + إلى + + + + From + من + + + + Subject + الموضوع + + + + Received + تاريخ الإستلام + + + + Inbox + البريد الوارد + + + + Load from Address book + تحميل من دفتر العناوين + + + + Message: + الرسالة: + + + + Subject: + الموضوع: + + + + Send to one or more specific people + إرسال لشخص أو عدة أشخاص + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + To: + إلى: + + + + From: + من: + + + + Broadcast to everyone who is subscribed to your address + إرسال لجميع المتابعين + + + + Send + إرسال + + + + Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. + إنتبه أن البث مشفر فقط بعنوانك، و يمكن لأي شخص يعرف عنوانك قراءة البث + + + + Status + الحالة + + + + Sent + البريد المرسل + + + + New + جديد + + + + Label (not shown to anyone) + إسم مستعار خاص - غير مرئي للآخرين + + + + Address + العنوان + + + + Group + المجموعة + + + + Stream + مجرى + + + + Your Identities + هوياتك + + + + Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. + هنا يمكن التسجيل لمتابعة مشاركات الآخرين، الرسائل ستظهر في البريد الوارد، و العناوين هنا تبطل العناوين في القائمة السوداء. + + + + Add new Subscription + إدخال إشتراك جديدة + + + + Label + إسم مستعار + + + + Subscriptions + الإشتراكات + + + + The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. + دفتر العناوين مفيد لإضافة أسماء أو طوابع بريدية للأشخاص الآخرين مع عناوينهم لكي يسهل تمييزهم بسهولة في البريد الوارد، يمكنك إضافة جهات اتصال جديدة باستخدام زر إضافة أو من البريد الوارد بالضغط الأيمن على الرسالة الواردة. + + + + Add new entry + إضافة جهة اتصال جديدة + + + + Name or Label + إسم مستعار + + + + Address Book + دفتر العناوين + + + + Use a Blacklist (Allow all incoming messages except those on the Blacklist) + إستخدام القائمة السوداء - تسمح وصول كل الرسائل الواردة عدا العناوين المسجلة في القائمة السوداء + + + + Use a Whitelist (Block all incoming messages except those on the Whitelist) + إستخدام القائمة البيضاء - تمنع وصول كل الرسائل الواردة عدا العناوين المسجلة في القائمة البيضاء + + + + Blacklist + القائمة السوداء + + + + Stream Number + Numéro de flux + + + + Number of Connections + Nombre de connexions + + + + Total connections: 0 + إجمالي الروابط + + + + Since startup at asdf: + منذ بداية التشغيل: + + + + Processed 0 person-to-person message. + تم معالجة 0 رسالة -شخص إلى شخص-. + + + + Processed 0 public key. + تم معالجة 0 مفتاح علني. + + + + Processed 0 broadcast. + تم معالجة 0 بث. + + + + Inventory lookups per second: 0 + معدل البحث ضمن المخزن لكل ثانية: 0 + + + + Network Status + حالة الشبكة + + + + File + ملف + + + + Settings + الضبط + + + + View + إظهار + + + + Hashtags + هاشتاق + + + + Help + مساعدة + + + + Import keys + إدراج المفاتيح + + + + Manage keys + إدارة المفاتيح + + + + Quit + الخروج + + + + About + عن + + + + Regenerate deterministic addresses + إعادة إنتاج عناوين حتمية - غير عشوائية + + + + Delete all trashed messages + حذف سلة المهملات + + + + Total Connections: %1 + إجمالي الروابط %1 + + + + Not Connected + غير متصل + + + + Connected + متصل + + + + Show Bitmessage + إظهار Bitmessage + + + + Subscribe + إشتراك + + + + Processed %1 person-to-person messages. + تم معالجة %1 من رسالة - شخص إلى شخص + + + + Processed %1 broadcast messages. + تم معالجة %1 من رسائل البث + + + + Processed %1 public keys. + تم معالجة %1 من المفاتيح العامة + + + + Since startup on %1 + منذ بداية التشغيل في %1 + + + + Waiting on their encryption key. Will request it again soon. + بانتظار مفتاح التشفير، سيتم طلبه مرة أخرى قريباً + + + + Encryption key request queued. + تم إدراح طلب مفتاح التشفير بقائمة الإنتظار. + + + + Queued. + تم الإدراج بقائمة الانتظار + + + + Need to do work to send message. Work is queued. + تحتاج لبعض العمل لإرسال الرسالة، تم إدراج العمل بقائمة الانتظار + + + + Acknowledgement of the message received %1 + تم استلام إشعار الاستلام للرسالة %1 + + + + Broadcast queued. + تم إدراج البث في قائمة الانتظار + + + + Broadcast on %1 + البث في %1 + + + + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 + مشكلة: العمل المطلوب من قبل المستلم أصعب من ما كنت مستعد للقيام به %1 + + + + Forced difficulty override. Send should start soon. + تم تجازو الصعوبة قصراً، ستبدأ الإرسال قريباً + + + + Message sent. Waiting on acknowledgement. Sent at %1 + تم إرسال الرسالة، بانتظار إشعار الإستلام، تم الإرسال في %1 + + + + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. + يمكنك إدارة مفاتيحك بتعديل ملف keys.dat المحفوظ بنفس المجلد الخاص بالبرنامج، مهم جداً أن تحتفظ بنسخة إضافية للملف المذكور سلفاً. + + + + You may manage your keys by editing the keys.dat file stored in + %1 +It is important that you back up this file. + يمكنك إدارة مفاتيحك بواسطة تعديل ملف keys.dat المحفوظ في +%1 +مهم جداً أن تحتفظ بنسخة إضافية من هذا الملف. + + + + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) + يمكنك إدارة مفاتيحك بتعديل ملف keys.dat المحفوظ بنفس المجلد الخاص بالبرنامج، مهم جداً أن تحتفظ بنسخة إضافية للملف المذكور سلفاً. هل ترغب بفتح الملف الآن؟ تأكد من إغلاق البرنامج Bitmessage قبل تعديل الملف. + + + + You may manage your keys by editing the keys.dat file stored in + %1 +It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) + يمكنك إدارة مفاتيحك بواسطة تعديل ملف keys.dat المحفوظ في +%1 +مهم جداً أن تحتفظ بنسخة إضافية من هذا الملف. هل ترغب بفتح الملف الآن؟ تأكد من إغلاق البرنامج Bitmessage قبل تعديل الملف. + + + + Add sender to your Address Book + إضافة جهة اتصال لدفتر العناوين + + + + Move to Trash + حذف إلى سلة المهملات + + + + View HTML code as formatted text + إظهار نظام تشفير HTML كنص منسق + + + + Enable + تفعيل + + + + Disable + تعطيل + + + + Copy address to clipboard + نسخ العنوان إلى الحافظة + + + + Special address behavior... + سلوك عنوان خاص + + + + Send message to this address + أرسل رسالة لهذا العنوان + + + + Send message to this group + أرسل رسالة لهذه المجموعة + + + + Set avatar... + تغيير الصورة الرمزية + + + + Add New Address + إضافة جهة إتصال + + + + Delete + حذف + + + + Copy destination address to clipboard + نسخ عنوان المرسل إليه إلى الحافظة + + + + Force send + إرسال قصري + + + + Are you sure you want to delete all trashed messages? + هل أنت متأكد من رغبتك في حذف كل الرسائل من سلة المهملات؟ + + + + You must type your passphrase. If you don't have one then this is not the form for you. + يجب إدخال عبارة المرور، إن لم تكن لديك عبارة مرور، إذاً هذه ليست الطريقة المناسبة لك + + + + Delete trash? + حذف سلة المهملات؟ + + + + Open keys.dat? + فتح ملف keys.dat؟ + + + + bad passphrase + عبارة المرور غير جيدة + + + + Restart + إعادة تشغيل + + + + You must restart Bitmessage for the port number change to take effect. + لتفعيل تغيير رقم نقطة العبور (port) يجب عليك إعادة تشغيل برنامج Bitmessage. + + + + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections. + Bitmessage utilisera votre proxy à partir de maintenant mais il vous faudra redémarrer Bitmessage pour fermer les connexions existantes. + + + + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. + خطأ: لا يمكنك إضافة نفس العنوان مرتين إلى القائمة، يمكنك إعادة تسمية العنوان. + + + + The address you entered was invalid. Ignoring it. + العنوان الذي أدخلته غير صالح، سيتم تجاهله. + + + + Passphrase mismatch + عبارة المرور غير متطابقه + + + + The passphrase you entered twice doesn't match. Try again. + عبارة المرور التي أدخلتها مرتين غير متطابقه، أعد المحاولة. + + + + Choose a passphrase + اختر عبارة المرور + + + + You really do need a passphrase. + أنت بحاجة لعبارة مرور. + + + + All done. Closing user interface... + تم عمل اللازم، سيتم إغلاق واجهة المستخدم + + + + Address is gone + تم إنتاج العنوان + + + + Bitmessage cannot find your address %1. Perhaps you removed it? + لم يستطع Bitmessage العثور على عنوانك %1, ربما قمت بحذف العنوان؟ + + + + Address disabled + تم تعطيل العنوان + + + + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. + خطأ: العنوان المستخدم للإرسال منه معطل، يجب عليك تفعيله في تبويب "هوياتك" قبل استخدامه. + + + + Entry added to the Address Book. Edit the label to your liking. + تم إضافة جهة الاتصال لدفتر العناوين، يمكنك تعديل الإسم المستعار إذا أحببت. + + + + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. + خطأ: لا يمكنك إضافة نفس العنوان إلى دفتر العناوين مرتين، يمكنك إعادة تسمية العنوان. + + + + Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. + تم نقل المادة لسلة المهملات، لا يتوفر واجهة مستخدم لإظهار سلة المهملات حالياً، و لكن يمكنك إيجاد الرسالة المحذوفة على القرص الصلب إذا أردت استرجاعها. + + + + No addresses selected. + لم يتم اختيار عناوين + + + + Options have been disabled because they either aren't applicable or because they haven't yet been implimented for your operating system. + Certaines options ont été désactivées car elles n'étaient pas applicables ou car elles n'ont pas encore été implémentées pour votre système d'exploitation. + + + + The address should start with ''BM-'' + العنوان يجب أن يبدأ ب "BM-". + + + + The address is not typed or copied correctly (the checksum failed). + لم يتم إدخال أو نسخ العنوان بالطريقة الصحيحة - اختبار checksum فشل. + + + + The version number of this address is higher than this software can support. Please upgrade Bitmessage. + رقم إصدار هذا العنوان أعلى من إمكانية هذا البرنامج، قم بتحديث البرنامج. + + + + The address contains invalid characters. + العنوان يحتوي على حروف غير صحيحة + + + + Some data encoded in the address is too short. + بعض البيانات المشفرة ضمن العنوان قصيرة جداً + + + + Some data encoded in the address is too long. + بعض البيانات المشفرة ضمن العنوان طويلة جداً. + + + + Address is valid. + العنوان صحيح + + + + You are using TCP port %1. (This can be changed in the settings). + أنت تستخدم نقطة عبور TCP %1 - يمكنك تغييره في قائمة الضبط. + + + + Error: Bitmessage addresses start with BM- Please check %1 + خطأ: عناوين ال Bitmessage تبدأ ب BM-، يرجى فحص %1 + + + + Error: The address %1 contains invalid characters. Please check it. + خطأ: العنوان %1 يحتوي على حروف غير صالحة، يرجى فحصه. + + + + Error: The address %1 is not typed or copied correctly. Please check it. + خطأ: لم يتم إدخال أو نسخ العنوان %1 بطريقة صحيحة، يرجى فحصه. + + + + Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. + خطأ: رقم إصدار العنوان %1 عالي جداً، إما أن تقوم بتحديث برنامج Bitmessage أو أن شريكك ذكي جدأ. + + + + Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance. + بعض البيانات المشفرة ضمن العنوان %1 قصيرة جداً. يمكن أن يكون هناك خطأ في برنامج شريكك. + + + + Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance. + بعض البيانات المشفرة ضمن العنوان %1 طويلة جداً. يمكن أن يكون هناك خطأ في برنامج شريكك. + + + + Error: Something is wrong with the address %1. + خطأ: هناك خطأ في هذا العنوان %1. + + + + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. + خطأ: يجب اختيار عنوان للإرسال منه، إن لم يكن لديك واحد إذهب إلى تبويب "هوياتك". + + + + Sending to your address + يتم الإرسال إلى عنوانك + + + + Error: One of the addresses to which you are sending a message, %1, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM. + خطأ: عنوان من العناوين المرسل إليها، %1, يكون لك، لسوئ الحظ عميل Bitmessage لا يمكنه معالجة رسالئه، يرجى تشغيل عميل ثاني في حاسوب آخر أو ضمن حاسوب إفتراضي. + + + + Address version number + رقم إصدار العنوان + + + + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. + بالنظر إلى العنوان %1, Bitmessage لم يستطع فهم رقم إصدار العنوان %2، ربما يجب عليك تحديث برنامج Bitmessage لإصداره الأخير. + + + + Stream number + رقم المجرى + + + + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. + بالنظر إلى العنوان %1, Bitmessage لم يستطع فهم رقم إصدار العنوان %2، ربما يجب عليك تحديث برنامج Bitmessage لإصداره الأخير. + + + + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. + تحذير: أنت غير متصل حالياً، Bitmessage سيقوم بالعمل اللازم لإرسال الرسالة و لكن لن يقوم بالإرسال حتى تصبح متصلاً. + + + + Your 'To' field is empty. + حقل "إلى" فارغ. + + + + Right click one or more entries in your address book and select 'Send message to this address'. + أنقر يميناً على واحد أو أكثر من جهات الاتصال في دفتر العناوين و اختر "إرسال رسالة لهذا العنوان". + + + + Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want. + خطأ: لا يمكنك إضافة نفس العنوان إلى الإشتراكات مرتين، يمكنك إعادة تسمية العنوان. + + + + Message trashed + تم حذف الرسالة + + + + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? + واحد من العناوين، %1، حاصل على رقم إصدار 1، العناوين ذات رقم الإصدار 1 غير مدعومه حالياً، هل باستطاعتنا حذفه الآن؟ + + + + Unknown status: %1 %2 + حالة غير معروفه: %1 %2 + + + + Connection lost + تم فقد الاتصال + + + + SOCKS5 Authentication problem: %1 + Problème d'authentification SOCKS5 : %1 + + + + Reply + . + رد + + + + Generating one new address + Génération d'une nouvelle adresse + + + + Done generating address. Doing work necessary to broadcast it... + Génération de l'adresse terminée. Travail pour la diffuser en cours... + + + + Done generating address + Génération de l'adresse terminée + + + + Message sent. Waiting on acknowledgement. Sent on %1 + Message envoyé. En attente de l'accusé de réception. Envoyé le %1 + + + + Error! Could not find sender address (your address) in the keys.dat file. + Erreur ! L'adresse de l'expéditeur (vous) n'a pas pu être trouvée dans le fichier keys.dat. + + + + Doing work necessary to send broadcast... + Travail pour envoyer la diffusion en cours... + + + + Broadcast sent on %1 + Message de diffusion envoyé le %1 + + + + Looking up the receiver's public key + Recherche de la clé publique du destinataire + + + + Doing work necessary to send message. (There is no required difficulty for version 2 addresses like this.) + Travail nécessaire pour envoyer le message en cours. (Il n'y a pas de difficulté requise pour ces adresses de version 2.) + + + + Doing work necessary to send message. +Receiver's required difficulty: %1 and %2 + Travail nécessaire pour envoyer le message. +Difficulté requise par le destinataire : %1 et %2 + + + + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. + Problème : Le travail demandé par le destinataire (%1 et %2) est plus difficile que ce que vous souhaitez faire. + + + + Work is queued. + تم إدراج العمل ضمن قائمة الإنتظار. + + + + Work is queued. %1 + تم إدراج العمل ضمن قائمة الإنتظار. %1 + + + + Doing work necessary to send message. +There is no required difficulty for version 2 addresses like this. + Travail nécessaire pour envoyer le message en cours. +Il n'y a pas de difficulté requise pour ces adresses de version 2. + + + + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 + مشكلة: مفتاح تشفير المرسل إليه غير جيد، لا يمكن تشفير الرسالة. %1 + + + + Save message as... + حفظ الرسالة ك + + + + Mark Unread + وضع علامة غير مقروء + + + + Subscribe to this address + متابعة هذا العنوان + + + + Message sent. Sent at %1 + تم إرسال الرسالة في %1 + + + + Chan name needed + مطلوب إسم زمرة + + + + You didn't enter a chan name. + لم تدخل إسم الزمرة + + + + Address already present + العنوان موجود سلفاً + + + + Could not add chan because it appears to already be one of your identities. + لا يمكن إضافة هذه الزمرة لأنها تعتبر أحد هوياتك. + + + + Success + نجاح + + + + Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'. + تم تكوين زمرة بنجاح، لإتاحة الفرصة للأخرين بالإنضمام لمجموعتك أعطهم إسم الزمرة و هذا العنوان %1، هذا العنوان سيظهر ضمن هوياتك. + + + + Address too new + العنوان جديد جداً + + + + Although that Bitmessage address might be valid, its version number is too new for us to handle. Perhaps you need to upgrade Bitmessage. + بالرغم أن العنوان صحيح و لكن رقم إصداره جديد جدًا بحيث لا يمكن التعامل معه، ربما عليك تحديث البرنامج. + + + + Address invalid + العنوان غير صحيح + + + + That Bitmessage address is not valid. + عنوان Bitmessage غير صحيح. + + + + Address does not match chan name + العنوان لا يتوافق مع إسم الزمرة + + + + Although the Bitmessage address you entered was valid, it doesn't match the chan name. + بالرغم أن العنوان صحيح، و لكن لا يتوافق مع إسم الزمرة. + + + + Successfully joined chan. + تم الإنضمام للزمرة بنجاح. + + + + Fetched address from namecoin identity. + تم تحصيل العنوان من هوية namecoin. + + + + New Message + رسالة جديدة + + + + From + من + + + + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). + سيقوم Bitmessage باستخدام البروكسي الخاص بك من الآن فصاعداً و لكن يمكنك إعادة تشغيل Bitmessage يدوياً لإغلاق الروابط الحالية -إن وجدت. + + + + Save As... + حفظ بإسم + + + + Write error. + خطأ كتابة. + + + + Options have been disabled because they either aren't applicable or because they haven't yet been implemented for your operating system. + تم تعطيل الخيارات لأنه إما أنها غير قابلة للتطبيق أو لم يتم برمجتها لنظام التشغيل الخاص بك. + + + + Testing... + اختبار... + + + + This is a chan address. You cannot use it as a pseudo-mailing list. + هذا عنوان الزمرة، لا يمكنك إستخدامه كقائمة بريدية مستعاره. + + + + Search + بحث + + + + All + الكل + + + + Message + الرسالة + + + + Fetch Namecoin ID + إحضار هوية namecoin + + + + Stream # + المجرى # + + + + Connections + الروابط + + + + Ctrl+Q + Ctrl+Q + + + + F1 + F1 + + + + Join / Create chan + إنضمام / تكوين زمرة + + + + MainWindows + + + Address is valid. + L'adresse est valide. + + + + NewAddressDialog + + + Create new Address + إنتاج عنوان جديد + + + + Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a "deterministic" address. +The 'Random Number' option is selected by default but deterministic addresses have several pros and cons: + هنا يمكنك إنتاج العديد من العناوين كما ترغب، في الواقع يفضل إنتاج و طرح العناوين، يمكنك إنتاج العناوين باستخدام أرقام عشوائية أو باستخدام عبارة مرور، إذا استخدمت عبارة مرور عندها يسمى العنوان عنوان حتمي. +خيار الرقم العشوائي هو الإفتراضي و لكن العناوين الحتمية لها عدد من الحسنات و السيئات: + + + + <html><head/><body><p><span style=" font-weight:600;">Pros:<br/></span>You can recreate your addresses on any computer from memory. <br/>You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. <br/><span style=" font-weight:600;">Cons:<br/></span>You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost. <br/>You must remember the address version number and the stream number along with your passphrase. <br/>If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.</p></body></html> + + + + + Use a random number generator to make an address + استخدم صانع الرقم العشوائي لإنتاج عنوان + + + + Use a passphrase to make addresses + استخدم عبارة المرور لإنتاج عناوين + + + + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter + خذ بعض دقائق إضافية من وقت الحساب لتقصير العنوان أو العناوين حرف أو حرفين + + + + Make deterministic addresses + أصنع عناوين حتمية + + + + Address version number: 3 + رقم إصدار العنوان: 3 + + + + In addition to your passphrase, you must remember these numbers: + بالإضافة لعبارة المرور، يجب عليك تذكر هذه الأرقام: + + + + Passphrase + عبارة المرور + + + + Number of addresses to make based on your passphrase: + عدد العناوين الناتجة إعتماداً على عبارة المرور الخاصه بك: + + + + Stream number: 1 + رقم المجرى: 1 + + + + Retype passphrase + أعد إدخال عبارة المرور + + + + Randomly generate address + أنتج العنوان عشوائياً + + + + Label (not shown to anyone except you) + إسم مستعار - غير مرئي لأي شخص سواك + + + + Use the most available stream + استخدم أكثر المجاري توفراً + + + + (best if this is the first of many addresses you will create) + الأفضل لو أن هذا العنوان هو الأول من عدة عناوين ستقوم بصنعها + + + + Use the same stream as an existing address + استخدم نفس المجرى مثل العنوان الموجود + + + + (saves you some bandwidth and processing power) + يوفر عليك بعض النطاق و القوة المعالجة + + + + NewSubscriptionDialog + + + Add new entry + إضافة مدخل جديد + + + + Label + إسم مستعار + + + + Address + عنوان + + + + SpecialAddressBehaviorDialog + + + Special Address Behavior + سلوك عنوان خاص + + + + Behave as a normal address + تصرف كعنوان عادي + + + + Behave as a pseudo-mailing-list address + تصرف كعنوان لقائمة بريدية مستعاره + + + + Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public). + سيتم بحث البريد الوارد للقائمة البريدية المستعاره لكل المتابعين بشكل آلي - و عليه سيكون علني. + + + + Name of the pseudo-mailing-list: + إسم القائمة البريدية المستعار: + + + + aboutDialog + + + PyBitmessage + PyBitmessage + + + + version ? + الإصدار ؟ + + + + About + عن البرنامج + + + + Copyright © 2013 Jonathan Warren + حقوق الحفظ © 2013 Warren Jonathan + + + + <html><head/><body><p>Distributed under the MIT/X11 software license; see <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> + + + + + This is Beta software. + هذه نسخة تجريبة للبرنامج + + + + connectDialog + + + Bitmessage + Bitmessage + + + + Bitmessage won't connect to anyone until you let it. + لن يقوم Bitmessage بالاتصال بأي أحد حتى تسمح له بذلك. + + + + Connect now + الاتصال الآن + + + + Let me configure special network settings first + أسمح لي بتعديل ضبط الشبكة الخاص أولاً + + + + helpDialog + + + Help + مساعدة + + + + <a href="http://Bitmessage.org/wiki/PyBitmessage_Help">http://Bitmessage.org/wiki/PyBitmessage_Help</a> + + + + + As Bitmessage is a collaborative project, help can be found online in the Bitmessage Wiki: + باعتبار أن برنامج Bitmessage هو مشروع تعاوني، يمكنك إيجاد المساعدة في Bitmessage Wiki: + + + + iconGlossaryDialog + + + Icon Glossary + فهرس الأيقونات + + + + You have no connections with other peers. + لا تمتلك روابط مع باقي الأقران -المشاركين- + + + + You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn't configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node. + لديك على الأقل رابط اتصال واحد مع الأقران - المشاركين - باستخدام رابط راحل و لكن حتى الآن لم يصلك رابط وارد، ربما جدارك الناري أو موجه بيتك غير مجهز لتوجيه روابط TCP الوارده لحاسوبك، Bitmessage سيعمل بشكل صحيح و لكن يمكنك مساعدة شبكة Bitmessage لو سمحت بوصول الروابط الوارده و سيساعدك لتصبح أكثر اتصالاً بالشبكة. + + + + You are using TCP port ?. (This can be changed in the settings). + أنت تستخدم نقطة عبور TCP ؟ يمكن تغيير هذه النقطة في الضبط + + + + You do have connections with other peers and your firewall is correctly configured. + أن على اتصال بباقي الأقران (المشاركين) و تم تضبيط الجدار الناري بطريقة صحيحة. + + + + newChanDialog + + + Dialog + الحوار + + + + Create a new chan + تكوين زمرة جديدة + + + + Join a chan + الإنضمام لزمرة + + + + Create a chan + تكوين زمرة + + + + <html><head/><body><p>Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. If you and someone else both create a chan with the same chan name then it is currently very likely that they will be the same chan.</p></body></html> + + + + + Chan name: + إسم الزمرة: + + + + <html><head/><body><p>A chan exists when a group of people share the same decryption keys. The keys and bitmessage address used by a chan are generated from a human-friendly word or phrase (the chan name). To send a message to everyone in the chan, send a normal person-to-person message to the chan address.</p><p>Chans are experimental and completely unmoderatable.</p></body></html> + الزمرة تتكون من مجموعة من الأشخاص يتشاركون في مفاتيح فك التشفير، يتم إنتاج المفاتيح و العناوين المستخدمة في الزمرة باستخدام كلمات أو جمل مفهومه للبشر، لإرسال رسالة لأفراد الزمرة قم بإرسال رسالة فردية لعنوان الزمرة، الزمر ما زالت تحت التجربة و غير مكتمله. + + + + Chan bitmessage address: + عنوان الزمرة: + + + + regenerateAddressesDialog + + + Regenerate Existing Addresses + إعادة إنتاج عناوين موجوده + + + + Regenerate existing addresses + إعادة إنتاج عناوين موجوده + + + + Passphrase + عبارة المرور + + + + Number of addresses to make based on your passphrase: + عدد العناوين المنتجه إعتماداً على عبارة المرور الخاصه بك: + + + + Address version number: + رقم إصدار العنوان: + + + + Stream number: + رقم المجرى: + + + + 1 + 1 + + + + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter + خذ بعض دقائق إضافية من وقت الحساب لتقصير العنوان أو العناوين حرف أو حرفين + + + + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. + يجب عليك اختيار أو عدم اختيار هذا الصندوق مثل ما قمت عن إنتاج عناوينك لأول مره. + + + + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. + إذا قمت بإنتاج عناوين حتمية و لكن تم فقدهم بسبب حادث - مثل فشل في القرص الصلب -، يمكنك إعادة إنتاجهم هنا، إما إذا قمت باستخدام مولّد الأرقام العشوائية لإنتاج عناوينك عندها هذه الطريقة غير مفيده لك. + + + + settingsDialog + + + Settings + الضبط + + + + Start Bitmessage on user login + إبدأ برنامج Bitmessage عند نقطة ولوج المستخدم + + + + Start Bitmessage in the tray (don't show main window) + تشغيل البرنامج في شريط المهام + + + + Minimize to tray + تصغير إلى شريط المهام + + + + Show notification when message received + أظهر التنبيهات عن وصول رسالة + + + + Run in Portable Mode + شغّل بالنظام المتنقل + + + + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. + في النظام المتنقل تكون الرسائل و ملفات الضبط محفوظة في مجلد البرنامج نفسه على خلاف بيانات البرنامج العادي، و بذلك يسهل تشغيل البرنامج من USB. + + + + User Interface + واجهة المستخدم + + + + Use Identicons + استخدم Identicons + + + + Interface Language + لغة العرض + + + + Listening port + نقطة عبور للإستماع + + + + Listen for connections on port: + استماع للروابط في نقطة عبور: + + + + Proxy server / Tor + خادم البروكسي / تور + + + + Type: + نوع: + + + + none + لا يوجد + + + + SOCKS4a + SOCKS4a + + + + SOCKS5 + SOCKS5 + + + + Server hostname: + إسم الخادم: + + + + Port: + نقطة عبور: + + + + Authentication + إثبات الهوية + + + + Username: + إسم المستخدم: + + + + Pass: + العبور: + + + + Network Settings + ضبط الشبكة + + + + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. + عندما يقوم أحد المشاركين بإرسال رسالة لك يقوم حاسوبه بأداء بعض العمل، صعوبة هذا العمل هو 1، يمكنك زيادة هذا الرقم الإفتراضي للعناوين الجديدة بتغيير القيم هنا، لكل عنوان جديد على المرسل أن يصل على صعوبة أعلى، باستثناء المشاركين الذين قمت بإضافتهم إلى دفتر عناوينك، البرنامج سيقوم تلقائياً بتنبيه هؤلاء المشاركين عند قيامك بإرسال رسالة بأن عليهم إكمال أقل كمية من العمل: الصعوبة 1. + + + + Total difficulty: + الصعوبة الإجمالية: + + + + Small message difficulty: + صعوبة الرسالة الصغيرة: + + + + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. + تقريبا كل صعوبات الرسائل الصغيرة تؤثر فقط على صعوبة إرسال الرسائل الصغيرة، بتضاعف هذه القيمة يجعلها تقريباً مرتين أصعب لإرسال رسالة ضغيرة و لكن لا تؤثر على الرسائل كبيرة الحجم. + + + + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. + الصعوبة الكلية تؤثر على الكمية المطلقة للعمل اللازم إكماله من قبل المرسل. تضاعف هذه القيمة يضاعف كمية العمل. + + + + Demanded difficulty + الصعوبة المطلوبة + + + + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. + هنا يمكنك تحديد الكمية القصوى من العمل الذي ترغب بأدائه عندما ترسل رسالة لشخص آخر، تصفير هذه القيم يدل على قبولك بأي قيمة. + + + + Maximum acceptable total difficulty: + الصعوبة الكلية القصوى المقبولة: + + + + Maximum acceptable small message difficulty: + صعوبة الرسائل الصغيرة القصوى المقبولة: + + + + Max acceptable difficulty + الصعوبة القصوى المقبولة + + + + Willingly include unencrypted destination address when sending to a mobile device + فضلاً أضف عنوان غير مشفر للمرسل إليه عندما ترسل إلى جهاز نقال + + + + Override automatic language localization (use countycode or language code, e.g. 'en_US' or 'en'): + تجاهل تحديد اللغة الآلي - استخدم رمز البلد أو رمز اللغة مثل 'en_US' أو 'en'-: + + + + Listen for incoming connections when using proxy + أنصت للروابط الوارده عن استخدام البروكسي + + + + Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to test. (Getting your own Bitmessage address into Namecoin is still rather difficult). Bitmessage can use either namecoind directly or a running nmcontrol instance. + يستطيع برنامج Bitmessage استخدام برنامج مختلف يعتمد على Bitcoin و يسمى Namecoin لإنتاج عناوين سهله التداول بين البشر، على سيبل المثال بدلاً من أن تقوم بإخبار صديقك عن عنوانك Bitmessage الطويل، بإمكانك أن تطلب منه إرسال رسالة للإختبار، إدخال عنوانك الخاص إلى Namecoin يبقى صعب بالمقارنة. برنامج Bitmessage إما أن يستخدم namecoind مباشره أو يقوم بتشغيل طلب nmcontrol. + + + + Host: + المضيف: + + + + Password: + كلمة العبور: + + + + Test + اختبار + + + + Connect to: + متصل ب: + + + + Namecoind + Namecoind + + + + NMControl + NMControl + + + + Namecoin integration + دمج Namecoin + + + + By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months. Leave these input fields blank for the default behavior. + إفتراضياً إذا أرسلت رسالة لشخص و هو غير متصل لأكثر من يومين سيقوم البرنامج بإرسال الرسالة مرة أخرى بعد يومين إضافيين، ستستمر عملية إعادة الإرسال بصور متباعده زمنياً، و عليه سيتم إعادة إرسال الرسائل بعد 5، 10، 20 يوم إلخ حتى يقوم المستلم بإرسال إشعار استلام الرسائل، هنا يمكنك تغيير أسلوب إعادة الإرسال و التوقف بعد عدة أيام أو شهور، أترك هذه الخانات فارغة لتفعيل الأسلوب الإفتراضي. + + + + Give up after + توقف بعد + + + + and + و + + + + days + أيام + + + + months. + شهور. + + + + Resends Expire + إنتهاء صلاحية إعادة الإرسال + + + + hashtagDialog + + + Hashtag + هاشتاق + + + + Trending Hashtags + الهاشتاقات النشطة + + + + Day + يوم + + + + Week + أسبوع + + + + Month + شهر + + + + All Time + كل الأوقات + + + + Popularity + الشعبية + + + + NewGroupDialog + + + Add new entry + إضافة مدخل جديد + + + + Label + الإسم المستعار + + + + Address + العنوان + + + + Group Name + إسم المجموعة + + + diff --git a/src/translations/bitmessage_de.pro b/src/translations/bitmessage_de.pro index 1e8fe836..56e2b4f1 100644 --- a/src/translations/bitmessage_de.pro +++ b/src/translations/bitmessage_de.pro @@ -2,6 +2,7 @@ SOURCES = ../addresses.py\ ../bitmessagemain.py\ ../class_addressGenerator.py\ ../class_outgoingSynSender.py\ + ../class_objectProcessor.py\ ../class_receiveDataThread.py\ ../class_sendDataThread.py\ ../class_singleCleaner.py\ @@ -17,6 +18,7 @@ SOURCES = ../addresses.py\ ../shared.py\ ../bitmessageqt/__init__.py\ ../bitmessageqt/about.py\ + ../bitmessageqt/addaddressdialog.py\ ../bitmessageqt/bitmessageui.py\ ../bitmessageqt/connect.py\ ../bitmessageqt/help.py\ diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index 210e4027..880c9e67 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -1,201 +1,217 @@ - - + + + AddAddressDialog + + + Add new entry + Neuen Eintrag erstellen + + + + Label + + + + + Address + Adresse + + MainWindow - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Eine Ihrer Adressen, %1, ist eine alte Version 1 Adresse. Version 1 Adressen werden nicht mehr unterstützt. Soll sie jetzt gelöscht werden? - + Reply - . Antworten - + Add sender to your Address Book Absender zum Adressbuch hinzufügen - + Move to Trash In den Papierkorb verschieben - + View HTML code as formatted text HTML als formatierten Text anzeigen - + Save message as... Nachricht speichern unter... - + New Neu - + Enable Aktivieren - + Disable Deaktivieren - + Copy address to clipboard Adresse in die Zwischenablage kopieren - + Special address behavior... Spezielles Verhalten der Adresse... - + Send message to this address Nachricht an diese Adresse senden - + Subscribe to this address Diese Adresse abonnieren - + Add New Address Neue Adresse hinzufügen - + Delete Löschen - + Copy destination address to clipboard Zieladresse in die Zwischenablage kopieren - + Force send Senden erzwingen - + Add new entry Neuen Eintrag erstellen - + Waiting for their encryption key. Will request it again soon. Warte auf den Verschlüsselungscode. Wird bald erneut angefordert. - + Encryption key request queued. - Verschlüsselungscode Anforderung steht aus. + Verschlüsselungscode-Anforderung steht aus. - + Queued. In Warteschlange. - + Message sent. Waiting for acknowledgement. Sent at %1 Nachricht gesendet. Warten auf Bestätigung. Gesendet %1 - + Need to do work to send message. Work is queued. Es muss Arbeit verrichtet werden um die Nachricht zu versenden. Arbeit ist in Warteschlange. - + Acknowledgement of the message received %1 Bestätigung der Nachricht erhalten %1 - + Broadcast queued. Rundruf in Warteschlange. - + Broadcast on %1 Rundruf um %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problem: Die vom Empfänger geforderte Arbeit ist schwerer als Sie bereit sind zu berechnen. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problem: Der Verschlüsselungscode des Empfängers ist nicht in Ordnung. Nachricht konnte nicht verschlüsselt werden. %1 - + Forced difficulty override. Send should start soon. Schwierigkeitslimit überschrieben. Senden sollte bald beginnen. - + Unknown status: %1 %2 Unbekannter Status: %1 %2 - + Since startup on %1 Seit Start der Anwendung am %1 - + Not Connected Nicht verbunden - + Show Bitmessage Bitmessage anzeigen - + Send Senden - + Subscribe Abonnieren - + Address Book Adressbuch - + Quit Schließen - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat Datei bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist wichtig, dass Sie diese Datei sichern. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -204,17 +220,17 @@ It is important that you back up this file. Es ist wichtig, dass Sie diese Datei sichern. - + Open keys.dat? Datei keys.dat öffnen? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat Datei bearbeiten, die im gleichen Ordner wie das Programm liegt. Es ist wichtig, dass Sie diese Datei sichern. Möchten Sie die Datei jetzt öffnen? (Stellen Sie sicher, dass Bitmessage geschlossen ist, bevor Sie etwas ändern.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -224,192 +240,192 @@ Es ist wichtig, dass Sie diese Datei sichern. Möchten Sie die datei jetzt öffn (Stellen Sie sicher, dass Bitmessage geschlossen ist, bevor Sie etwas ändern.) - + Delete trash? Papierkorb leeren? - + Are you sure you want to delete all trashed messages? Sind Sie sicher, dass Sie alle Nachrichten im Papierkorb löschen möchten? - + bad passphrase Falscher Passwort-Satz - + You must type your passphrase. If you don't have one then this is not the form for you. Sie müssen Ihren Passwort-Satz eingeben. Wenn Sie keinen haben, ist dies das falsche Formular für Sie. - + Processed %1 person-to-person messages. %1 Person-zu-Person-Nachrichten bearbeitet. - + Processed %1 broadcast messages. %1 Rundruf-Nachrichten bearbeitet. - + Processed %1 public keys. %1 öffentliche Schlüssel bearbeitet. - + Total Connections: %1 Verbindungen insgesamt: %1 - + Connection lost Verbindung verloren - + Connected Verbunden - + Message trashed Nachricht in den Papierkorb verschoben - + Error: Bitmessage addresses start with BM- Please check %1 Fehler: Bitmessage Adressen starten mit BM- Bitte überprüfen Sie %1 - + Error: The address %1 is not typed or copied correctly. Please check it. Fehler: Die Adresse %1 wurde nicht korrekt getippt oder kopiert. Bitte überprüfen. - + Error: The address %1 contains invalid characters. Please check it. Fehler: Die Adresse %1 beinhaltet ungültig Zeichen. Bitte überprüfen. - + Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Fehler: Die Adressversion von %1 ist zu hoch. Entweder Sie müssen Ihre Bitmessage Software aktualisieren oder Ihr Bekannter ist sehr clever. - + Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Adresse %1 codiert sind, sind zu kurz. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance. Fehler: Einige Daten die in der Adresse %1 codiert sind, sind zu lang. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt. - + Error: Something is wrong with the address %1. Fehler: Mit der Adresse %1 stimmt etwas nicht. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Fehler: Sie müssen eine Absenderadresse auswählen. Sollten Sie keine haben, wechseln Sie zum Reiter "Ihre Identitäten". Sending to your address - Sende zu Ihrer Adresse + Sende zu Ihrer Adresse Error: One of the addresses to which you are sending a message, %1, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM. - Fehler: Eine der Adressen an die Sie eine Nachricht schreiben (%1) ist Ihre. Leider kann die Bitmessage Software ihre eigenen Nachrichten nicht verarbeiten. Bitte verwenden Sie einen zweite Installation auf einem anderen Computer oder in einer VM. + Fehler: Eine der Adressen an die Sie eine Nachricht schreiben (%1) ist Ihre. Leider kann die Bitmessage Software ihre eigenen Nachrichten nicht verarbeiten. Bitte verwenden Sie einen zweite Installation auf einem anderen Computer oder in einer VM. - + Address version number Adressversion - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. Bezüglich der Adresse %1, Bitmessage kann Adressen mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Stream number Datenstrom Nummer - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. Bezüglich der Adresse %1, Bitmessage kann den Datenstrom mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Warnung: Sie sind aktuell nicht verbunden. Bitmessage wird die nötige Arbeit zum versenden verrichten, aber erst senden, wenn Sie verbunden sind. - + Your 'To' field is empty. Ihr "Empfänger"-Feld ist leer. Work is queued. - Arbeit in Warteschlange. + Arbeit in Warteschlange. - + Right click one or more entries in your address book and select 'Send message to this address'. Klicken Sie mit rechts auf eine oder mehrere Einträge aus Ihrem Adressbuch und wählen Sie "Nachricht an diese Adresse senden". - + Work is queued. %1 Arbeit in Warteschlange. %1 - + New Message Neue Nachricht - + From - Von + Von - + Address is valid. Adresse ist gültig. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Fehler: Sie können eine Adresse nicht doppelt im Adressbuch speichern. Wenn Sie möchten, benennen Sie den existierenden Eintrag um. - + The address you entered was invalid. Ignoring it. Die von Ihnen eingegebene Adresse ist ungültig, sie wird ignoriert. - + Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want. Fehler: Sie können eine Adresse nicht doppelt abonnieren. Wenn Sie möchten, benennen Sie den existierenden Eintrag um. - + Restart Neustart - + You must restart Bitmessage for the port number change to take effect. Sie müssen Bitmessage neu starten, um den geänderten Port zu verwenden. @@ -419,168 +435,168 @@ Es ist wichtig, dass Sie diese Datei sichern. Möchten Sie die datei jetzt öffn Bitmessage wird den Proxy-Server ab jetzt verwenden, möglicherweise möchten Sie Bitmessage neu starten um bestehende Verbindungen zu schließen. - + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. Fehler: Sie können eine Adresse nicht doppelt zur Liste hinzufügen. Wenn Sie möchten, benennen Sie den existierenden Eintrag um. - + Passphrase mismatch Kennwortsatz nicht identisch - + The passphrase you entered twice doesn't match. Try again. Die von Ihnen eingegebenen Kennwortsätze sind nicht identisch. Bitte neu versuchen. - + Choose a passphrase Wählen Sie einen Kennwortsatz - + You really do need a passphrase. Sie benötigen wirklich einen Kennwortsatz. - + All done. Closing user interface... Alles fertig. Benutzer interface wird geschlossen... - + Address is gone Adresse ist verloren - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmassage kann Ihre Adresse %1 nicht finden. Haben Sie sie gelöscht? - + Address disabled Adresse deaktiviert - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Fehler: Die Adresse von der Sie versuchen zu senden ist deaktiviert. Sie müssen sie unter dem Reiter "Ihre Identitäten" aktivieren bevor Sie fortfahren. - + Entry added to the Address Book. Edit the label to your liking. Eintrag dem Adressbuch hinzugefügt. Editieren Sie den Eintrag nach Belieben. - + Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. Objekt in den Papierkorb verschoben. Es gibt kein Benutzerinterface für den Papierkorb, aber die Daten sind noch auf Ihrer Festplatte wenn Sie sie wirklich benötigen. - + Save As... Speichern unter... - + Write error. Fehler beim speichern. - + No addresses selected. Keine Adresse ausgewählt. Options have been disabled because they either aren't applicable or because they haven't yet been implemented for your operating system. - + Optionen wurden deaktiviert, da sie für Ihr Betriebssystem nicht relevant, oder noch nicht implementiert sind. - + The address should start with ''BM-'' Die Adresse sollte mit "BM-" beginnen - + The address is not typed or copied correctly (the checksum failed). Die Adresse wurde nicht korrekt getippt oder kopiert (Prüfsumme falsch). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Die Versionsnummer dieser Adresse ist höher als diese Software unterstützt. Bitte installieren Sie die neuste Bitmessage Version. - + The address contains invalid characters. Diese Adresse beinhaltet ungültige Zeichen. - + Some data encoded in the address is too short. Die in der Adresse codierten Daten sind zu kurz. - + Some data encoded in the address is too long. Die in der Adresse codierten Daten sind zu lang. - + You are using TCP port %1. (This can be changed in the settings). Sie benutzen TCP-Port %1 (Dieser kann in den Einstellungen verändert werden). - + Bitmessage Bitmessage - + To An - + From Von - + Subject Betreff - + Received Erhalten - + Inbox Posteingang - + Load from Address book Aus Adressbuch wählen - + Message: Nachricht: - + Subject: Betreff: - + Send to one or more specific people Nachricht an eine oder mehrere spezifische Personen @@ -591,284 +607,284 @@ Optionen wurden deaktiviert, da sie für Ihr Betriebssystem nicht relevant, oder p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - + To: An: - + From: Von: - + Broadcast to everyone who is subscribed to your address Rundruf an jeden, der Ihre Adresse abonniert hat - + Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. Beachten Sie, dass Rudrufe nur mit Ihrer Adresse verschlüsselt werden. Jeder, der Ihre Adresse kennt, kann diese Nachrichten lesen. - + Status Status - + Sent Gesendet - + Label (not shown to anyone) Bezeichnung (wird niemandem gezeigt) - + Address Adresse - + Stream Datenstrom - + Your Identities Ihre Identitäten - + Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. Hier können Sie "Rundruf Nachrichten" abonnieren, die von anderen Benutzern versendet werden. Die Nachrichten tauchen in Ihrem Posteingang auf. (Die Adressen hier überschreiben die auf der Blacklist). - + Add new Subscription Neues Abonnement anlegen - + Label Bezeichnung - + Subscriptions Abonnements - + The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. Das Adressbuch ist nützlich um die Bitmessage-Adressen anderer Personen Namen oder Beschreibungen zuzuordnen, so dass Sie sie einfacher im Posteingang erkennen können. Sie können Adressen über "Hinzufügen" eintragen, oder über einen Rechtsklick auf eine Nachricht im Posteingang. - + Name or Label Name oder Bezeichnung - + Use a Blacklist (Allow all incoming messages except those on the Blacklist) Liste als Blacklist verwenden (Erlaubt alle eingehenden Nachrichten, außer von Adressen auf der Blacklist) - + Use a Whitelist (Block all incoming messages except those on the Whitelist) Liste als Whitelist verwenden (Erlaubt keine eingehenden Nachrichten, außer von Adressen auf der Whitelist) - + Blacklist Blacklist - + Stream # Datenstrom # - + Connections Verbindungen - + Total connections: 0 Verbindungen insgesamt: 0 - + Since startup at asdf: Seit start um asdf: - + Processed 0 person-to-person message. 0 Person-zu-Person-Nachrichten verarbeitet. - + Processed 0 public key. 0 öffentliche Schlüssel verarbeitet. - + Processed 0 broadcast. 0 Rundrufe verarbeitet. - + Network Status Netzwerk status - + File Datei - + Settings Einstellungen - + Help Hilfe - + Import keys Schlüssel importieren - + Manage keys Schlüssel verwalten - + About Über - + Regenerate deterministic addresses Deterministische Adressen neu generieren - + Delete all trashed messages Alle Nachrichten im Papierkorb löschen - + Message sent. Sent at %1 Nachricht gesendet. gesendet am %1 - + Chan name needed Chan name benötigt - + You didn't enter a chan name. Sie haben keinen Chan-Namen eingegeben. - + Address already present Adresse bereits vorhanden - + Could not add chan because it appears to already be one of your identities. Chan konnte nicht erstellt werden, da es sich bereits um eine Ihrer Identitäten handelt. - + Success Erfolgreich - + Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'. Chan erfolgreich erstellt. Um andere diesem Chan beitreten zu lassen, geben Sie ihnen den Chan-Namen und die Bitmessage-Adresse: %1. Diese Adresse befindet sich auch unter "Ihre Identitäten". - + Address too new Adresse zu neu - + Although that Bitmessage address might be valid, its version number is too new for us to handle. Perhaps you need to upgrade Bitmessage. Obwohl diese Bitmessage-Adresse gültig ist, ist ihre Versionsnummer zu hoch um verarbeitet zu werden. Vermutlich müssen Sie eine neuere Version von Bitmessage installieren. - + Address invalid Adresse ungültig - + That Bitmessage address is not valid. Diese Bitmessage-Adresse ist nicht gültig. - + Address does not match chan name Adresse stimmt nicht mit dem Chan-Namen überein - + Although the Bitmessage address you entered was valid, it doesn't match the chan name. Obwohl die Bitmessage-Adresse die Sie eingegeben haben gültig ist, stimmt diese nicht mit dem Chan-Namen überein. - + Successfully joined chan. Chan erfolgreich beigetreten. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage wird ab sofort den Proxy-Server verwenden, aber eventuell möchten Sie Bitmessage neu starten um bereits bestehende Verbindungen zu schließen. - + This is a chan address. You cannot use it as a pseudo-mailing list. Dies ist eine Chan-Adresse. Sie können sie nicht als Pseudo-Mailingliste verwenden. - + Search Suchen - + All Alle - + Message Nachricht - + Join / Create chan Chan beitreten / erstellen @@ -898,35 +914,134 @@ p, li { white-space: pre-wrap; } Anfrag für den Verschlüsselungscode gesendet. Warte auf Antwort. Angefragt am %1 - + Mark Unread Als ungelesen markieren - + Fetched address from namecoin identity. Adresse aus Namecoin Identität geholt. - + Testing... teste... - + Fetch Namecoin ID Hole Namecoin ID - + Ctrl+Q Strg+Q - + F1 F1 + + + Set avatar... + + + + + Bad address version number + + + + + Your address version number must be a number: either 3 or 4. + + + + + Your address version number must be either 3 or 4. + + + + + Inventory lookups per second: %1 + + + + + Will not resend ever + + + + + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. + + + + + Do you really want to remove this avatar? + + + + + You have already set an avatar for this address. Do you really want to overwrite it? + + + + + Start-on-login not yet supported on your OS. + + + + + Minimize-to-tray not yet supported on your OS. + + + + + Tray notifications not yet supported on your OS. + + + + + Enter an address above. + + + + + Address is an old type. We cannot display its past broadcasts. + + + + + There are no recent broadcasts from this address to display. + + + + + Display the %1 recent broadcast from this address. + + + + + Display the %1 recent broadcasts from this address. + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + + + + + Inventory lookups per second: 0 + + NewAddressDialog @@ -945,7 +1060,7 @@ Die Zufallszahlen-Option ist standard, jedoch haben deterministische Adressen ei <html><head/><body><p><span style=" font-weight:600;">Pros:<br/></span>You can recreate your addresses on any computer from memory. <br/>You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. <br/><span style=" font-weight:600;">Cons:<br/></span>You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost. <br/>You must remember the address version number and the stream number along with your passphrase. <br/>If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.</p></body></html> - <html><head/><body><p><span style=" font-weight:600;">Vorteile:<br/></span>Sie können ihre Adresse an jedem Computer aus dem Gedächtnis regenerieren. <br/>Sie brauchen sich keine Sorgen um das Sichern ihrer Schlüssel machen solange Sie sich den Kennwortsatz merken. <br/><span style=" font-weight:600;">Nachteile:<br/></span>Sie müssen sich den Kennowrtsatz merken (oder aufschreiben) wenn Sie in der Lage sein wollen ihre Schlüssel wiederherzustellen. <br/>Sie müssen sich die Adressversion und die Datenstrom Nummer zusammen mit dem Kennwortsatz merken. <br/>Wenn Sie einen schwachen Kennwortsatz wählen und jemand kann ihn erraten oder durch ausprobieren herausbekommen, kann dieser Ihre Nachrichten lesen, oder in ihrem Namen Nachrichten senden..</p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Vorteile:<br/></span>Sie können ihre Adresse an jedem Computer aus dem Gedächtnis regenerieren. <br/>Sie brauchen sich keine Sorgen um das Sichern ihrer Schlüssel machen solange Sie sich den Kennwortsatz merken. <br/><span style=" font-weight:600;">Nachteile:<br/></span>Sie müssen sich den Kennwortsatz merken (oder aufschreiben) wenn Sie in der Lage sein wollen ihre Schlüssel wiederherzustellen. <br/>Sie müssen sich die Adressversion und die Datenstrom Nummer zusammen mit dem Kennwortsatz merken. <br/>Wenn Sie einen schwachen Kennwortsatz wählen und jemand kann ihn erraten oder durch ausprobieren herausbekommen, kann dieser Ihre Nachrichten lesen, oder in ihrem Namen Nachrichten senden..</p></body></html> @@ -970,7 +1085,7 @@ Die Zufallszahlen-Option ist standard, jedoch haben deterministische Adressen ei Address version number: 3 - Adress-Versionsnummer: 3 + Adress-Versionsnummer: 3 @@ -1027,24 +1142,34 @@ Die Zufallszahlen-Option ist standard, jedoch haben deterministische Adressen ei (saves you some bandwidth and processing power) (Dies erspart Ihnen etwas an Bandbreite und Rechenleistung) + + + Address version number: 4 + Adress-Versionsnummer: 4 + NewSubscriptionDialog - + Add new entry Neuen Eintrag erstellen - + Label Name oder Bezeichnung - + Address Adresse + + + CheckBox + + SpecialAddressBehaviorDialog @@ -1077,35 +1202,40 @@ Die Zufallszahlen-Option ist standard, jedoch haben deterministische Adressen ei aboutDialog - + About Über - + PyBitmessage PyBitmessage - + version ? Version ? - + Copyright © 2013 Jonathan Warren - Copyright © 2013 Jonathan Warren + Copyright © 2013 Jonathan Warren - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <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> <html><head/><body><p>Veröffentlicht unter der MIT/X11 Software-Lizenz; see <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> - + This is Beta software. Diese ist Beta-Software. + + + <html><head/><body><p>Copyright © 2012-2013 Jonathan Warren<br/>Copyright © 2013 The Bitmessage Developers</p></body></html> + + connectDialog @@ -1222,272 +1352,377 @@ Die Zufallszahlen-Option ist standard, jedoch haben deterministische Adressen ei regenerateAddressesDialog - + Regenerate Existing Addresses Bestehende Adresse regenerieren - + Regenerate existing addresses Bestehende Adresse regenerieren - + Passphrase Kennwortsatz - + Number of addresses to make based on your passphrase: Anzahl der Adressen die basierend auf diesem Kennwortsatz erzeugt werden sollen: Address version Number: - Adress-Versionsnummer: + Adress-Versionsnummer: 3 - 3 + 3 - + Stream number: Stream Nummer: - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Verwenden Sie einige Minuten extra Rechenleistung um die Adresse(n) ein bis zwei Zeichen kürzer zu machen - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. Sie müssen diese Option auswählen (oder nicht auswählen) wie Sie es gemacht haben, als Sie Ihre Adresse das erste mal erstellt haben. - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. Wenn Sie bereits deterministische Adressen erstellt haben, aber diese durch einen Unfall wie eine defekte Festplatte verloren haben, können Sie sie hier regenerieren. Wenn Sie den Zufallsgenerator verwendet haben um Ihre Adressen erstmals zu erstellen, kann dieses Formular Ihnen nicht helfen. + + + Address version number: + + settingsDialog - + Settings Einstellungen - + Start Bitmessage on user login Bitmessage nach dem Hochfahren automatisch starten - + Start Bitmessage in the tray (don't show main window) Bitmessage minimiert starten (Zeigt das Hauptfenster nicht an) - + Minimize to tray In den Systemtray minimieren - + Show notification when message received Benachrichtigung anzeigen, wenn eine Nachricht eintrifft - + Run in Portable Mode In portablem Modus arbeiten - + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. Im portablen Modus werden Nachrichten und Konfigurationen im gleichen Ordner abgelegt, wie sich das Programm selbst befindet anstelle im normalen Anwendungsdaten-Ordner. Das macht es möglich Bitmessage auf einem USB-Stick zu betreiben. - + User Interface Benutzerinterface - + Listening port TCP-Port - + Listen for connections on port: Wartet auf Verbindungen auf Port: - + Proxy server / Tor Proxy-Server / Tor - + Type: Typ: - + none keiner - + SOCKS4a SOCKS4a - + SOCKS5 SOCKS5 - + Server hostname: Servername: - + Port: Port: - + Authentication Authentifizierung - + Username: Benutzername: - + Pass: Kennwort: - + Network Settings Netzwerkeinstellungen - + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. Wenn jemand Ihnen eine Nachricht schickt, muss der absendende Computer erst einige Arbeit verrichten. Die Schwierigkeit dieser Arbeit ist standardmäßig 1. Sie können diesen Wert für alle neuen Adressen, die Sie generieren hier ändern. Es gibt eine Ausnahme: Wenn Sie einen Freund oder Bekannten in Ihr Adressbuch übernehmen, wird Bitmessage ihn mit der nächsten Nachricht automatisch informieren, dass er nur noch die minimale Arbeit verrichten muss: Schwierigkeit 1. - + Total difficulty: Gesamtschwierigkeit: - + Small message difficulty: Schwierigkeit für kurze Nachrichten: - + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. Die "Schwierigkeit für kurze Nachrichten" trifft nur auf das senden kurzen Nachrichten zu. Verdoppelung des Wertes macht es fast doppelt so schwer kurze Nachrichten zu senden, aber hat keinen Effekt bei langen Nachrichten. - + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. Die "Gesammtschwierigkeit" beeinflusst die absolute menge Arbeit die ein Sender verrichten muss. Verdoppelung dieses Wertes verdoppelt die Menge der Arbeit. - + Demanded difficulty Geforderte Schwierigkeit - + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. Hier setzen Sie die maximale Arbeit, die Sie bereit sind zu verrichten, um eine Nachricht an eine andere Person zu versenden. Ein Wert von 0 bedeutet, dass Sie jede Arbeit akzeptieren. - + Maximum acceptable total difficulty: Maximale akzeptierte Gesammtschwierigkeit: - + Maximum acceptable small message difficulty: Maximale akzeptierte Schwierigkeit für kurze Nachrichten: - + Max acceptable difficulty Maximale akzeptierte Schwierigkeit - + Listen for incoming connections when using proxy Auf eingehende Verdindungen warten, auch wenn eine Proxy-Server verwendet wird - + Willingly include unencrypted destination address when sending to a mobile device Willentlich die unverschlüsselte Adresse des Empfängers übertragen, wenn an ein mobiles Gerät gesendet wird - + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> <html><head/><body><p>Bitmessage kann ein anderes Bitcoin basiertes Programm namens Namecoin nutzen um Adressen leserlicher zu machen. Zum Beispiel: Anstelle Ihrem Bekannten Ihre lange Bitmessage-Adresse vorzulesen, können Sie ihm einfach sagen, er soll eine Nachricht an <span style=" font-style:italic;">test </span>senden.</p><p> (Ihre Bitmessage-Adresse in Namecoin zu speichern ist noch sehr umständlich)</p><p>Bitmessage kann direkt namecoind verwenden, oder eine nmcontrol Instanz.</p></body></html> - + Host: Server: - + Password: Kennwort: - + Test Verbindung testen - + Connect to: Verbinde mit: - + Namecoind Namecoind - + NMControl NMControl - + Namecoin integration Namecoin Integration Override automatic language localization (use countycode or language code, e.g. 'en_US' or 'en'): - Automatische Sprachauswahl überschreiben (verwenden Sie den Landescode oder Sprachcode, z.B. "de_DE" oder "de"): + Automatische Sprachauswahl überschreiben (verwenden Sie den Landescode oder Sprachcode, z.B. "de_DE" oder "de"): + + + + Use Identicons + + + + + Interface Language + + + + + System Settings + system + + + + + English + en + + + + + Esperanto + eo + + + + + Français + fr + + + + + Deutsch + de + + + + + Españl + es + + + + + русский язык + ru + + + + + Norsk + no + + + + + Pirate English + en_pirate + + + + + Other (set in keys.dat) + other + + + + + <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> + + + + + Give up after + + + + + and + + + + + days + + + + + months. + + + + + Resends Expire + diff --git a/src/translations/bitmessage_en_pirate.pro b/src/translations/bitmessage_en_pirate.pro index 5b7f27e4..acc712ee 100644 --- a/src/translations/bitmessage_en_pirate.pro +++ b/src/translations/bitmessage_en_pirate.pro @@ -2,6 +2,7 @@ SOURCES = ../addresses.py\ ../bitmessagemain.py\ ../class_addressGenerator.py\ ../class_outgoingSynSender.py\ + ../class_objectProcessor.py\ ../class_receiveDataThread.py\ ../class_sendDataThread.py\ ../class_singleCleaner.py\ @@ -17,6 +18,7 @@ SOURCES = ../addresses.py\ ../shared.py\ ../bitmessageqt/__init__.py\ ../bitmessageqt/about.py\ + ../bitmessageqt/addaddressdialog.py\ ../bitmessageqt/bitmessageui.py\ ../bitmessageqt/connect.py\ ../bitmessageqt/help.py\ diff --git a/src/translations/bitmessage_en_pirate.ts b/src/translations/bitmessage_en_pirate.ts index 3666aa88..ba97978a 100644 --- a/src/translations/bitmessage_en_pirate.ts +++ b/src/translations/bitmessage_en_pirate.ts @@ -1,892 +1,978 @@ - - + - MainWindow + AddAddressDialog - - One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? - - - - - Reply - . - - - - - Add sender to your Address Book - - - - - Move to Trash - - - - - View HTML code as formatted text - - - - - Save message as... - - - - - Mark Unread - - - - - New - - - - - Enable - - - - - Disable - - - - - Copy address to clipboard - - - - - Special address behavior... - - - - - Send message to this address - - - - - Subscribe to this address - - - - - Add New Address - - - - - Delete - - - - - Copy destination address to clipboard - - - - - Force send - - - - + Add new entry Add yee new entry - + + Label + Label + + + + Address + Address + + + + MainWindow + + + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? + + + + + Reply + + + + + Add sender to your Address Book + + + + + Move to Trash + + + + + View HTML code as formatted text + + + + + Save message as... + + + + + Mark Unread + + + + + New + + + + + Enable + + + + + Disable + + + + + Copy address to clipboard + + + + + Special address behavior... + + + + + Send message to this address + + + + + Subscribe to this address + + + + + Add New Address + + + + + Delete + + + + + Copy destination address to clipboard + + + + + Force send + + + + + Add new entry + Add yee new entry + + + Since startup on %1 - + Waiting for their encryption key. Will request it again soon. - + Encryption key request queued. - + Queued. - + Message sent. Waiting for acknowledgement. Sent at %1 - + Message sent. Sent at %1 - + Need to do work to send message. Work is queued. - + Acknowledgement of the message received %1 - + Broadcast queued. - + Broadcast on %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 - + Forced difficulty override. Send should start soon. - + Unknown status: %1 %2 - + Not Connected - + Show Bitmessage - + Send - + Subscribe - + Address Book - + Quit - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. - + Open keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) - + Delete trash? - + Are you sure you want to delete all trashed messages? - + bad passphrase - + You must type your passphrase. If you don't have one then this is not the form for you. - + Chan name needed - + You didn't enter a chan name. - + Address already present - + Could not add chan because it appears to already be one of your identities. - + Success - + Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'. - + Address too new - + Although that Bitmessage address might be valid, its version number is too new for us to handle. Perhaps you need to upgrade Bitmessage. - + Address invalid - + That Bitmessage address is not valid. - + Address does not match chan name - + Although the Bitmessage address you entered was valid, it doesn't match the chan name. - + Successfully joined chan. - + Processed %1 person-to-person messages. - + Processed %1 broadcast messages. - + Processed %1 public keys. - + Total Connections: %1 - + Connection lost - + Connected - + Message trashed - + Error: Bitmessage addresses start with BM- Please check %1 - + Error: The address %1 is not typed or copied correctly. Please check it. - + Error: The address %1 contains invalid characters. Please check it. - + Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. - + Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance. - + Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance. - + Error: Something is wrong with the address %1. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. - - Sending to your address - - - - - Error: One of the addresses to which you are sending a message, %1, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM. - - - - + Address version number - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. - + Stream number - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. - + Your 'To' field is empty. - - Work is queued. - - - - + Right click one or more entries in your address book and select 'Send message to this address'. - + Fetched address from namecoin identity. - + Work is queued. %1 - + New Message - + From - + Address is valid. - + The address you entered was invalid. Ignoring it. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. - + Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want. - + Restart - + You must restart Bitmessage for the port number change to take effect. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). - + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. - + Passphrase mismatch - + The passphrase you entered twice doesn't match. Try again. - + Choose a passphrase - + You really do need a passphrase. - + All done. Closing user interface... - + Address is gone - + Bitmessage cannot find your address %1. Perhaps you removed it? - + Address disabled - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. - + Entry added to the Address Book. Edit the label to your liking. - + Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. - + Save As... - + Write error. - + No addresses selected. - - Options have been disabled because they either aren't applicable or because they haven't yet been implemented for your operating system. - - - - + Testing... - + This is a chan address. You cannot use it as a pseudo-mailing list. - + The address should start with ''BM-'' - + The address is not typed or copied correctly (the checksum failed). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. - + The address contains invalid characters. - + Some data encoded in the address is too short. - + Some data encoded in the address is too long. - + You are using TCP port %1. (This can be changed in the settings). - + Bitmessage - + Search - + All - + To - + From - + Subject - + Message - + Received - + Inbox - + Load from Address book - + Fetch Namecoin ID - + Message: - + Subject: - + Send to one or more specific people - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - - - - + To: - + From: - + Broadcast to everyone who is subscribed to your address - + Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. - + Status - + Sent - + Label (not shown to anyone) - + Address Address - + Stream - + Your Identities - + Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. - + Add new Subscription - + Label Label - + Subscriptions - + The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. - + Name or Label - + Use a Blacklist (Allow all incoming messages except those on the Blacklist) - + Use a Whitelist (Block all incoming messages except those on the Whitelist) - + Blacklist - + Stream # - + Connections - + Total connections: 0 - + Since startup at asdf: - + Processed 0 person-to-person message. - + Processed 0 public key. - + Processed 0 broadcast. - + Network Status - + File - + Settings Settings - + Help Help - + Import keys - + Manage keys - + Ctrl+Q Ctrrl+Q - + F1 - + About - + Regenerate deterministic addresses - + Delete all trashed messages - + Join / Create chan + + + Set avatar... + + + + + Bad address version number + + + + + Your address version number must be a number: either 3 or 4. + + + + + Your address version number must be either 3 or 4. + + + + + Inventory lookups per second: %1 + + + + + Will not resend ever + + + + + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. + + + + + Do you really want to remove this avatar? + + + + + You have already set an avatar for this address. Do you really want to overwrite it? + + + + + Start-on-login not yet supported on your OS. + + + + + Minimize-to-tray not yet supported on your OS. + + + + + Tray notifications not yet supported on your OS. + + + + + Enter an address above. + + + + + Address is an old type. We cannot display its past broadcasts. + + + + + There are no recent broadcasts from this address to display. + + + + + Display the %1 recent broadcast from this address. + + + + + Display the %1 recent broadcasts from this address. + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + + + + + Inventory lookups per second: 0 + + NewAddressDialog @@ -930,7 +1016,7 @@ T' 'Random Number' option be selected by default but deterministi Address version number: 3 - Arrddress version number: 3 + Arrddress version number: 3 @@ -992,24 +1078,34 @@ T' 'Random Number' option be selected by default but deterministi Use a passphrase to make addresses + + + Address version number: 4 + Arrddress version number: 4 + NewSubscriptionDialog - + Add new entry Add yee new entry - + Label Label - + Address Address + + + CheckBox + + SpecialAddressBehaviorDialog @@ -1042,35 +1138,35 @@ T' 'Random Number' option be selected by default but deterministi aboutDialog - + PyBitmessage PyBitmessage - + version ? Version ? - + About - - - Copyright © 2013 Jonathan Warren - - - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <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> - + This is Beta software. + + + <html><head/><body><p>Copyright © 2012-2013 Jonathan Warren<br/>Copyright © 2013 The Bitmessage Developers</p></body></html> + + connectDialog @@ -1192,272 +1288,372 @@ T' 'Random Number' option be selected by default but deterministi regenerateAddressesDialog - + Regenerate Existing Addresses Regenerate Existin' Arrddresses - + Regenerate existing addresses Regenerate existin' addresses - + Passphrase Passphrase - + Number of addresses to make based on your passphrase: Number of arrddresses to make based on yee passphrase: Address version Number: - Arrddress version Number: + Arrddress version Number: 3 - 3 + 3 - + Stream number: Stream numberrr: - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Spend several minutes extra computin' time to make yee address(es) 1 arr 2 characters sharter - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. Yee must check (arr not check) this box just like yee did (or didn't) when yee made your arrddresses the first time. - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. If yee have previously made deterministic arrddresses but yee lost them due to an accident (like losin' yee pirate ship), yee can regenerate them here. If yee used t' random number generator to make yee addresses then this form be of no use to you. + + + Address version number: + + settingsDialog - + Settings Settings - + Start Bitmessage on user login Start yee Bitmessage on userrr login - + Start Bitmessage in the tray (don't show main window) Start yee Bitmessage in t' tray (don't show main window) - + Minimize to tray Minimize to yee tray - + Show notification when message received Show yee a notification when message received - + Run in Portable Mode Run in yee Portable Mode - + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. In Portable Mode, messages and config files are stored in t' same directory as yee program rather than t' normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive or wooden leg. - + User Interface User Interface - + Listening port Listenin' port - + Listen for connections on port: Listen for connections on yee port: - + Proxy server / Tor Proxy server / Tor - + Type: Type: - + none none - + SOCKS4a SOCKS4a - + SOCKS5 SOCKS5 - + Server hostname: Server hostname: - + Port: Port: - + Authentication Authentication - + Username: Username: - + Pass: Pass: - + Network Settings Network Settings - + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. When a pirate sends yee a message, their computer must first complete a load of work. T' difficulty of t' work, by default, is 1. Yee may raise this default for new arrddresses yee create by changin' the values here. Any new arrddresses you be createin' will require senders to meet t' higher difficulty. There be one exception: if yee add a friend or pirate to yee arrddress book, Bitmessage be automatically notifyin' them when yee next send a message that they needin' be only complete t' minimum amount of work: difficulty 1. - + Total difficulty: Total difficulty: - + Small message difficulty: Small message difficulty: - + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. T' 'Small message difficulty' mostly only affects t' difficulty of sending small messages. Doubling this value be makin' it almost twice as difficult to send a small message but doesn't really affect large messages. - + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. T' 'Total difficulty' affects the absolute amount of work yee sender must complete. Doubling this value be doublin' t' amount of work. - + Demanded difficulty Demanded difficulty - + Willingly include unencrypted destination address when sending to a mobile device - - Override automatic language localization (use countycode or language code, e.g. 'en_US' or 'en'): - - - - + Listen for incoming connections when using proxy - + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. - + Maximum acceptable total difficulty: - + Maximum acceptable small message difficulty: - + Max acceptable difficulty - + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> - + Host: - + Password: - + Test - + Connect to: - + Namecoind - + NMControl - + Namecoin integration + + + Use Identicons + + + + + Interface Language + + + + + System Settings + system + + + + + English + en + + + + + Esperanto + eo + + + + + Français + fr + + + + + Deutsch + de + + + + + Españl + es + + + + + русский язык + ru + + + + + Norsk + no + + + + + Pirate English + en_pirate + + + + + Other (set in keys.dat) + other + + + + + <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> + + + + + Give up after + + + + + and + + + + + days + + + + + months. + + + + + Resends Expire + + diff --git a/src/translations/bitmessage_eo.pro b/src/translations/bitmessage_eo.pro index 7a53b739..1e9492cd 100644 --- a/src/translations/bitmessage_eo.pro +++ b/src/translations/bitmessage_eo.pro @@ -1,6 +1,7 @@ SOURCES = ../addresses.py\ ../bitmessagemain.py\ ../class_addressGenerator.py\ + ../class_objectProcessor.py\ ../class_outgoingSynSender.py\ ../class_receiveDataThread.py\ ../class_sendDataThread.py\ @@ -17,6 +18,7 @@ SOURCES = ../addresses.py\ ../shared.py\ ../bitmessageqt/__init__.py\ ../bitmessageqt/about.py\ + ../bitmessageqt/addaddressdialog.py\ ../bitmessageqt/bitmessageui.py\ ../bitmessageqt/connect.py\ ../bitmessageqt/help.py\ diff --git a/src/translations/bitmessage_eo.ts b/src/translations/bitmessage_eo.ts index 816bd98c..2f09691d 100644 --- a/src/translations/bitmessage_eo.ts +++ b/src/translations/bitmessage_eo.ts @@ -1,208 +1,218 @@ - - + + + AddAddressDialog + + + Add new entry + Aldoni novan elementon + + + + Label + Etikedo + + + + Address + Adreso + + MainWindow - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Iu de viaj adresoj, %1, estas malnova versio 1 adreso. Ĉu ni povas forviŝi ĝin? - + Reply - Aŭ ĉu oni uzas u-formon ĉi tie? "Respondu"?! Ŝajnas al mi ke ne.. Respondi - + Add sender to your Address Book Aldoni sendinton al via adresaro - + Move to Trash Movi al rubujo - + View HTML code as formatted text Montri HTML-n kiel aranĝita teksto - + Save message as... Konservi mesaĝon kiel... - + New Nova - + Enable - ankaŭ eblus aktivigi Ŝalti - + Disable - ankaŭ eblus malaktivigi Malŝalti - + Copy address to clipboard - ankaŭ eblus "tondujo" aŭ "poŝo" Kopii adreson al tondejo - + Special address behavior... Speciala sinteno de adreso... - + Send message to this address Sendi mesaĝon al tiu adreso - + Subscribe to this address Aboni tiun adreson - + Add New Address Aldoni novan adreson - + Delete - aŭ forigi Forviŝi - + Copy destination address to clipboard Kopii cel-adreson al tondejo - + Force send Devigi sendadon - + Add new entry - aŭ eron Aldoni novan elementon - + Waiting for their encryption key. Will request it again soon. mi ne certas kiel traduki "their" ĉi tie. Sonas strange al mi. Atendante al ilia ĉifroŝlosilo. Baldaŭ petos ĝin denove. - + Encryption key request queued. Peto por ĉifroŝlosilo envicigita. - + Queued. En atendovico. - + Message sent. Waiting for acknowledgement. Sent at %1 Mesaĝo sendita. Atendante konfirmon. Sendita je %1 - + Need to do work to send message. Work is queued. Devas labori por sendi mesaĝon. Laboro en atendovico. - + Acknowledgement of the message received %1 Ricevis konfirmon de la mesaĝo je %1 - + Broadcast queued. Elsendo en atendovico. - + Broadcast on %1 Elsendo je %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problemo: la demandita laboro de la ricevonto estas pli malfacila ol vi pretas fari. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Problemo: la ĉifroŝlosilo de la ricevonto estas rompita. Ne povis ĉifri la mesaĝon. %1 - + Forced difficulty override. Send should start soon. - Ĉi tie mi ne certas kiel traduki "Forced difficulty override" Devigita superado de limito de malfacilaĵo. Sendado devus baldaŭ komenci. - + Unknown status: %1 %2 Nekonata stato: %1 %2 - + Since startup on %1 Ekde lanĉo de la programo je %1 - + Not Connected Ne konektita - + Show Bitmessage Montri Bitmesaĝon - + Send Sendi - + Subscribe Aboni - + Address Book Adresaro - + Quit Eliri - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Vi povas administri viajn ŝlosilojn redaktante la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava ke vi faru savkopion de tiu dosiero. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -211,18 +221,17 @@ It is important that you back up this file. Estas grava ke vi faru savkopion de tiu dosiero. - + Open keys.dat? - Ĉu "ĉu" estas bezonata ĉi tie? Aŭ ĉu sufiĉas diri "Malfermi keys.dat?" Ĉu malfermi keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Vi povas administri viajn ŝlosilojn redaktante la dosieron keys.dat en la sama dosierujo kiel tiu programo. Estas grava ke vi faru savkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -231,197 +240,192 @@ It is important that you back up this file. Would you like to open the file now? Estas grava ke vi faru savkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.) - + Delete trash? Malplenigi rubujon? - + Are you sure you want to delete all trashed messages? - Ĉu "el" aŭ "en" pli taŭgas? Ĉu vi certas ke vi volas forviŝi ĉiujn mesaĝojn el la rubojo? - + bad passphrase malprava pasvorto - + You must type your passphrase. If you don't have one then this is not the form for you. Vi devas tajpi vian pasvorton. Se vi ne havas pasvorton tiu ne estas la prava formularo por vi. - + Processed %1 person-to-person messages. - (Mi trovis "inter-para" kiel traduko de P2P en vikipedio: https://eo.wikipedia.org/wiki/P2p "samtavola" sonis strange al mi. Inter "p"ara ja povus uziĝi kiel "para-al-para" kvazaŭ kiel la angla P2P. Poste mi vidis ke tio ne celas peer2peer sed person2person.) Pritraktis %1 inter-personajn mesaĝojn. - + Processed %1 broadcast messages. Pritraktis %1 elsendojn. - + Processed %1 public keys. Pritraktis %1 publikajn ŝlosilojn. - + Total Connections: %1 - Ĉu "totala" pravas ĉi tie? Totalaj Konektoj: %1 - + Connection lost Perdis konekton - + Connected Konektita - + Message trashed Movis mesaĝon al rubujo - + Error: Bitmessage addresses start with BM- Please check %1 Eraro: en Bitmesaĝa adresoj komencas kun BM- Bonvolu kontroli %1 - + Error: The address %1 is not typed or copied correctly. Please check it. Eraro: La adreso %1 ne estis prave tajpita aŭ kopiita. Bonvolu kontroli ĝin. - + Error: The address %1 contains invalid characters. Please check it. Eraro: La adreso %1 enhavas malpermesitajn simbolojn. Bonvolu kontroli ĝin. - + Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. - Mi demandis en DevTalk kiel traduki " is being clever." Ŝajne la aliaj tradukoj simple forlasis ĝin. Eraro: La adres-versio %1 estas tro alta. Eble vi devas promocii vian Bitmesaĝo programon aŭ via konato uzas alian programon. - + Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance. Eraro: Kelkaj datumoj kodita en la adreso %1 estas tro mallongaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance. Eraro: Kelkaj datumoj kodita en la adreso %1 estas tro longaj. Povus esti ke io en la programo de via konato malfunkcias. - + Error: Something is wrong with the address %1. Eraro: Io malĝustas kun la adreso %1. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Eraro: Vi devas elekti sendontan adreson. Se vi ne havas iun, iru al langeto "Viaj identigoj". Sending to your address - Sendante al via adreso + Sendante al via adreso Error: One of the addresses to which you are sending a message, %1, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM. - Ĉu "kliento" pravas? Ĉu "virtuala maŝino? - Eraro: Unu el la adresoj al kiuj vi sendas mesaĝon (%1) apartenas al vi. Bedaŭrinde, la kliento de Bitmesaĝo ne povas pritrakti siajn proprajn mesaĝojn. Bonvolu uzi duan klienton ĉe alia komputilo aŭ en virtuala maŝino (VM). + Eraro: Unu el la adresoj al kiuj vi sendas mesaĝon (%1) apartenas al vi. Bedaŭrinde, la kliento de Bitmesaĝo ne povas pritrakti siajn proprajn mesaĝojn. Bonvolu uzi duan klienton ĉe alia komputilo aŭ en virtuala maŝino (VM). - + Address version number Numero de adresversio - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. - + Stream number Fluo numero - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. - + Your 'To' field is empty. Via "Ricevonto"-kampo malplenas. Work is queued. - Laboro en atendovico. + Laboro en atendovico. - + Right click one or more entries in your address book and select 'Send message to this address'. - + Work is queued. %1 Laboro en atendovico. %1 - + New Message Nova mesaĝo - + From De - + Address is valid. Adreso estas ĝusta. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Eraro: Vi ne povas duoble aldoni la saman adreson al via adresaro. Provu renomi la jaman se vi volas. - + The address you entered was invalid. Ignoring it. La adreso kiun vi enmetis estas malĝusta. Ignoras ĝin. - + Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want. Eraro: Vi ne povas duoble aboni la saman adreson. Provu renomi la jaman se vi volas. - + Restart Restartigi - + You must restart Bitmessage for the port number change to take effect. Vi devas restartigi Bitmesaĝon por ke la ŝanĝo de la numero de pordo (Port Number) efektivigu. @@ -431,175 +435,162 @@ Estas grava ke vi faru savkopion de tiu dosiero. Ĉu vi volas malfermi la dosier Bitmessage wird den Proxy-Server ab jetzt verwenden, möglicherweise möchten Sie Bitmessage neu starten um bestehende Verbindungen zu schließen. - + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. - + Passphrase mismatch - Notu ke pasfrazo kutime estas pli longa ol pasvorto. Pasfrazoj malsamas - + The passphrase you entered twice doesn't match. Try again. La pasfrazo kiun vi duoble enmetis malsamas. Provu denove. - + Choose a passphrase Elektu pasfrazon - + You really do need a passphrase. Vi ja vere bezonas pasfrazon. - + All done. Closing user interface... Ĉiu preta. Fermante fasadon... - + Address is gone - Oni devas certigi KIE tiu frazo estas por havi la kuntekston. Adreso foriris - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmesaĝo ne povas trovi vian adreson %1. Ĉu eble vi forviŝis ĝin? - + Address disabled Adreso malŝaltita - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Eraro: La adreso kun kiu vi provas sendi estas malŝaltita. Vi devos ĝin ŝalti en la langeto 'Viaj identigoj' antaŭ uzi ĝin. - + Entry added to the Address Book. Edit the label to your liking. - Ĉu pli bone "Bv. redakti"? Aldonis elementon al adresaro. Redaktu la etikedo laŭvole. - + Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. Movis elementojn al rubujo. Ne estas fasado por rigardi vian rubujon, sed ankoraŭ estas sur disko se vi esperas ĝin retrovi. - + Save As... Konservi kiel... - + Write error. - http://komputeko.net/index_en.php?vorto=write+error Skriberaro. - + No addresses selected. Neniu adreso elektita. - - Options have been disabled because they either aren't applicable or because they haven't yet been implemented for your operating system. - - - - + The address should start with ''BM-'' - Aŭ "devus komenci" laŭ kunteksto? La adreso komencu kun "BM-" - + The address is not typed or copied correctly (the checksum failed). La adreso ne estis prave tajpita aŭ kopiita (kontrolsumo malsukcesis). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. - + The address contains invalid characters. La adreso enhavas malpermesitajn simbolojn. - + Some data encoded in the address is too short. Kelkaj datumoj kodita en la adreso estas tro mallongaj. - + Some data encoded in the address is too long. Kelkaj datumoj kodita en la adreso estas tro longaj. - + You are using TCP port %1. (This can be changed in the settings). Vi estas uzanta TCP pordo %1 (Tio estas ŝanĝebla en la agordoj). - + Bitmessage - Dependas de la kunteksto ĉu oni volas traduki tion. Bitmesaĝo - + To Al - + From De - + Subject Temo - + Received - Kunteksto? Ricevita - + Inbox - aŭ "ricevkesto" http://komputeko.net/index_en.php?vorto=Inbox Ricevujo - + Load from Address book Ŝarĝi el adresaro - + Message: Mesaĝo: - + Subject: Temo: - + Send to one or more specific people Sendi al unu aŭ pli specifaj personoj @@ -610,296 +601,284 @@ Estas grava ke vi faru savkopion de tiu dosiero. Ĉu vi volas malfermi la dosier p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - + To: Al: - + From: De: - + Broadcast to everyone who is subscribed to your address - Ĉu "ĉiu kiu" eĉ eblas? Elsendi al ĉiu kiu subskribis al via adreso - + Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. Sciu ke elsendoj estas sole ĉifrita kun via adreso. Iu ajn povas legi ĝin se tiu scias vian adreson. - + Status - http://komputeko.net/index_en.php?vorto=status Stato - + Sent Sendita - + Label (not shown to anyone) Etikdeo (ne montrita al iu ajn) - + Address Adreso - + Stream Fluo - + Your Identities - Mi ne certis kion uzi: ĉu identeco (rim: internacia senco) aŭ ĉu identigo. Mi decidis por identigo pro la rilato al senco "rekoni" ("identigi krimulon") Via identigoj - + Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. Ĉi tie vi povas aboni "elsendajn mesaĝojn" elsendita de aliaj uzantoj. Adresoj ĉi tie transpasas tiujn sur la langeto "Nigara listo". - + Add new Subscription - kial "Subscription" kun granda S? Aldoni novan Abonon - + Label Etikedo - + Subscriptions Abonoj - + The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. La Adresaro estas utila por aldoni nomojn aŭ etikedojn al la Bitmesaĝa adresoj de aliaj persono por ke vi povu rekoni ilin pli facile en via ricevujo. Vi povas aldoni elementojn ĉi tie uzante la butonon 'Aldoni', aŭ en la ricevujo per dekstra klako al mesaĝo. - + Name or Label - e aŭ E? Nomo aŭ Etikedo - + Use a Blacklist (Allow all incoming messages except those on the Blacklist) - http://komputeko.net/index_en.php?vorto=blacklist Uzi Nigran Liston (permesi ĉiujn alvenintajn mesaĝojn escepte tiuj en la Nigra Listo) - + Use a Whitelist (Block all incoming messages except those on the Whitelist) - mi skribis majuskle ĉar mi pensas ke Blacklist and Whitelist estas specifa por tiu kunteksto Uzi Blankan Liston (bloki ĉiujn alvenintajn mesaĝojn escepte tiuj en la Blanka Listo) - + Blacklist Nigra Listo - + Stream # Fluo # - + Connections Konetkoj - + Total connections: 0 Totalaj konektoj: 0 - + Since startup at asdf: - Stranga fonto! Ekde lanĉo de la programo je asdf: - + Processed 0 person-to-person message. Pritraktis 0 inter-personajn mesaĝojn. - + Processed 0 public key. Pritraktis 0 publikajn ŝlosilojn. - + Processed 0 broadcast. Pritraktis 0 elsendojn. - + Network Status Reta Stato - + File Dosiero - + Settings Agordoj - + Help Helpo - + Import keys Importi ŝlosilojn - + Manage keys Administri ŝlosilojn - + About Pri - + Regenerate deterministic addresses - http://www.reta-vortaro.de/revo/art/gener.html https://eo.wikipedia.org/wiki/Determinismo Regeneri determinisman adreson - + Delete all trashed messages Forviŝi ĉiujn mesaĝojn el rubujo - + Message sent. Sent at %1 Mesaĝo sendita. Sendita je %1 - + Chan name needed - http://komputeko.net/index_en.php?vorto=channel Bezonas nomon de kanalo - + You didn't enter a chan name. Vi ne enmetis nonon de kanalo. - + Address already present - eble laŭ kunteksto "en la listo"? Adreso jam ĉi tie - + Could not add chan because it appears to already be one of your identities. Ne povis aldoni kanalon ĉar ŝajne jam estas unu el viaj indentigoj. - + Success Sukceso - + Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'. Sukcese kreis kanalon. Por ebligi al aliaj aniĝi vian kanalon, sciigu al ili la nomon de la kanalo kaj ties Bitmesaĝa adreso: %1. Tiu adreso ankaŭ aperas en 'Viaj identigoj'. - + Address too new - Kion tio signifu? kunteksto? Adreso tro nova - + Although that Bitmessage address might be valid, its version number is too new for us to handle. Perhaps you need to upgrade Bitmessage. Kvankam tiu Bitmesaĝa adreso povus esti ĝusta, ĝia versionumero estas tro nova por pritrakti ĝin. Eble vi devas promocii vian Bitmesaĝon. - + Address invalid Adreso estas malĝusta - + That Bitmessage address is not valid. Tiu Bitmesaĝa adreso ne estas ĝusta. - + Address does not match chan name Adreso ne kongruas kun kanalonomo - + Although the Bitmessage address you entered was valid, it doesn't match the chan name. Kvankam la Bitmesaĝa adreso kiun vi enigis estas ĝusta, ĝi ne kongruas kun la kanalonomo. - + Successfully joined chan. Sukcese aniĝis al kanalo. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmesaĝo uzos vian prokurilon (proxy) ekde nun sed eble vi volas permane restartigi Bitmesaĝon nun por ke ĝi fermu eblajn jamajn konektojn. - + This is a chan address. You cannot use it as a pseudo-mailing list. Tio estas kanaladreso. Vi ne povas ĝin uzi kiel pseŭdo-dissendolisto. - + Search Serĉi - + All Ĉio - + Message Mesaĝo - + Join / Create chan Aniĝi / Krei kanalon @@ -929,36 +908,134 @@ p, li { white-space: pre-wrap; } Anfrag für den Verschlüsselungscode gesendet. Warte auf Antwort. Angefragt am %1 - + Mark Unread Marki nelegita - + Fetched address from namecoin identity. Venigis adreson de Namecoin identigo. - + Testing... Testante... - + Fetch Namecoin ID Venigu Namecoin ID - + Ctrl+Q - http://komputeko.net/index_en.php?vorto=ctrl Stir+Q - + F1 F1 + + + Set avatar... + + + + + Bad address version number + + + + + Your address version number must be a number: either 3 or 4. + + + + + Your address version number must be either 3 or 4. + + + + + Inventory lookups per second: %1 + + + + + Will not resend ever + + + + + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. + + + + + Do you really want to remove this avatar? + + + + + You have already set an avatar for this address. Do you really want to overwrite it? + + + + + Start-on-login not yet supported on your OS. + + + + + Minimize-to-tray not yet supported on your OS. + + + + + Tray notifications not yet supported on your OS. + + + + + Enter an address above. + + + + + Address is an old type. We cannot display its past broadcasts. + + + + + There are no recent broadcasts from this address to display. + + + + + Display the %1 recent broadcast from this address. + + + + + Display the %1 recent broadcasts from this address. + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + + + + + Inventory lookups per second: 0 + + NewAddressDialog @@ -998,11 +1075,6 @@ The 'Random Number' option is selected by default but deterministic ad Make deterministic addresses - - - Address version number: 3 - - In addition to your passphrase, you must remember these numbers: @@ -1058,24 +1130,34 @@ The 'Random Number' option is selected by default but deterministic ad (saves you some bandwidth and processing power) + + + Address version number: 4 + + NewSubscriptionDialog - + Add new entry Aldoni novan elementon - + Label Etikedo - + Address Adreso + + + CheckBox + + SpecialAddressBehaviorDialog @@ -1108,35 +1190,40 @@ The 'Random Number' option is selected by default but deterministic ad aboutDialog - + About Pri - + PyBitmessage PyBitmessage - + version ? Veriso ? - + Copyright © 2013 Jonathan Warren - Kopirajto © 2013 Jonathan Warren + Kopirajto © 2013 Jonathan Warren - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <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> <html><head/><body><p>Distribuita sub la permesilo "MIT/X11 software license"; vidu <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> - + This is Beta software. Tio estas beta-eldono. + + + <html><head/><body><p>Copyright © 2012-2013 Jonathan Warren<br/>Copyright © 2013 The Bitmessage Developers</p></body></html> + + connectDialog @@ -1171,7 +1258,6 @@ The 'Random Number' option is selected by default but deterministic ad <a href="http://Bitmessage.org/wiki/PyBitmessage_Help">http://Bitmessage.org/wiki/PyBitmessage_Help</a> - Mi aldonis "(angle)" ĉar le enhavo de la helpopaĝo ja ne estas tradukita <a href="http://Bitmessage.org/wiki/PyBitmessage_Help">http://Bitmessage.org/wiki/PyBitmessage_Help (angle)</a> @@ -1254,272 +1340,377 @@ The 'Random Number' option is selected by default but deterministic ad regenerateAddressesDialog - + Regenerate Existing Addresses Regeneri ekzistantajn adresojn - + Regenerate existing addresses Regeneri ekzistantajn Adresojn - + Passphrase Pasfrazo - + Number of addresses to make based on your passphrase: Kvanto de farotaj adresoj bazante sur via pasfrazo: Address version Number: - Adresa versio numero: + Adresa versio numero: 3 - 3 + 3 - + Stream number: Fluo numero: - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Elspezi kelkajn minutojn per aldona tempo de komputila kalkulado por fari adreso(j)n 1 aŭ 2 simbolojn pli mallonge - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. Vi devas marki (aŭ ne marki) tiun markobutono samkiel vi faris kiam vi generis vian adreson la unuan fojon. - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. Se vi antaŭe kreis determinismajn adresojn sed perdis ĝin akcidente (ekz. en diska paneo), vi povas regeneri ilin ĉi tie. Se vi uzis la generilo de hazardaj numeroj por krei vian adreson tiu formularo ne taŭgos por vi. + + + Address version number: + + settingsDialog - + Settings Agordoj - + Start Bitmessage on user login Startigi Bitmesaĝon dum ensaluto de uzanto - + Start Bitmessage in the tray (don't show main window) Startigi Bitmesaĝon en la taskopleto (tray) ne montrante tiun fenestron - + Minimize to tray Plejetigi al taskopleto - + Show notification when message received Montri sciigon kiam mesaĝo alvenas - + Run in Portable Mode Ekzekucii en Portebla Reĝimo - + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. En Portebla Reĝimo, mesaĝoj kaj agordoj estas enmemorigitaj en la sama dosierujo kiel la programo mem anstataŭ en la dosierujo por datumoj de aplikaĵoj. Tio igas ĝin komforta ekzekucii Bitmesaĝon el USB poŝmemorilo. - + User Interface Fasado - + Listening port Aŭskultanta pordo (port) - + Listen for connections on port: Aŭskultu pri konektoj ĉe pordo: - + Proxy server / Tor Prokurila (proxy) servilo / Tor - + Type: Tipo: - + none Neniu - + SOCKS4a SOCKS4a - + SOCKS5 SOCKS5 - + Server hostname: Servilo gastiga nomo (hostname): - + Port: Pordo (port): - + Authentication Aŭtentigo - + Username: Uzantnomo: - + Pass: Pas: - + Network Settings Retaj agordoj - + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. - + Total difficulty: - + Small message difficulty: - + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. - + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. - + Demanded difficulty - + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. - + Maximum acceptable total difficulty: - + Maximum acceptable small message difficulty: - + Max acceptable difficulty - + Listen for incoming connections when using proxy - + Willingly include unencrypted destination address when sending to a mobile device - + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> - + Host: Gastiga servilo: - + Password: Pasvorto: - + Test Testo - + Connect to: Kenekti al: - + Namecoind Namecoind - + NMControl NMControl - + Namecoin integration Integrigo de Namecoin Override automatic language localization (use countycode or language code, e.g. 'en_US' or 'en'): - Transpasi la automatan reconon de locala lingvo (uzu landokodon aŭ lingvokodon, ekz. 'en_US' aŭ 'en'): + Transpasi la automatan reconon de locala lingvo (uzu landokodon aŭ lingvokodon, ekz. 'en_US' aŭ 'en'): + + + + Use Identicons + + + + + Interface Language + + + + + System Settings + system + + + + + English + en + + + + + Esperanto + eo + + + + + Français + fr + + + + + Deutsch + de + + + + + Españl + es + + + + + русский язык + ru + + + + + Norsk + no + + + + + Pirate English + en_pirate + + + + + Other (set in keys.dat) + other + + + + + <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> + + + + + Give up after + + + + + and + + + + + days + + + + + months. + + + + + Resends Expire + diff --git a/src/translations/bitmessage_fr.pro b/src/translations/bitmessage_fr.pro index f2215048..0e56c016 100644 --- a/src/translations/bitmessage_fr.pro +++ b/src/translations/bitmessage_fr.pro @@ -1,6 +1,7 @@ SOURCES = ../addresses.py\ ../bitmessagemain.py\ ../class_addressGenerator.py\ + ../class_objectProcessor.py\ ../class_outgoingSynSender.py\ ../class_receiveDataThread.py\ ../class_sendDataThread.py\ @@ -17,6 +18,7 @@ SOURCES = ../addresses.py\ ../shared.py\ ../bitmessageqt/__init__.py\ ../bitmessageqt/about.py\ + ../bitmessageqt/addaddressdialog.py\ ../bitmessageqt/bitmessageui.py\ ../bitmessageqt/connect.py\ ../bitmessageqt/help.py\ diff --git a/src/translations/bitmessage_fr.ts b/src/translations/bitmessage_fr.ts index 3d94a688..e32eb5ef 100644 --- a/src/translations/bitmessage_fr.ts +++ b/src/translations/bitmessage_fr.ts @@ -1,55 +1,72 @@ - - + + + AddAddressDialog + + + Add new entry + Ajouter une nouvelle entrée + + + + Label + Label + + + + Address + Adresse + + MainWindow - + Bitmessage Bitmessage - + To Vers - + From De - + Subject Sujet - + Received Reçu - + Inbox Boîte de réception - + Load from Address book Charger depuis carnet d'adresses - + Message: Message : - + Subject: Sujet : - + Send to one or more specific people Envoyer à une ou plusieurs personne(s) @@ -60,124 +77,124 @@ p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - + To: Vers : - + From: De : - + Broadcast to everyone who is subscribed to your address Diffuser à chaque abonné de cette adresse - + Send Envoyer - + Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. Gardez en tête que les diffusions sont seulement chiffrées avec votre adresse. Quiconque disposant de votre adresse peut les lire. - + Status Statut - + Sent Envoyé - + New Nouveau - + Label (not shown to anyone) Label (seulement visible par vous) - + Address Adresse - + Stream Flux - + Your Identities Vos identités - + Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. Vous pouvez ici souscrire aux 'messages de diffusion' envoyés par d'autres utilisateurs. Les messages apparaîtront dans votre boîte de récption. Les adresses placées ici outrepassent la liste noire. - + Add new Subscription Ajouter un nouvel abonnement - + Label Label - + Subscriptions Abonnements - + The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. Le carnet d'adresses est utile pour mettre un nom sur une adresse Bitmessage et ainsi faciliter la gestion de votre boîte de réception. Vous pouvez ajouter des entrées ici en utilisant le bouton 'Ajouter', ou depuis votre boîte de réception en faisant un clic-droit sur un message. - + Add new entry Ajouter une nouvelle entrée - + Name or Label Nom ou Label - + Address Book Carnet d'adresses - + Use a Blacklist (Allow all incoming messages except those on the Blacklist) Utiliser une liste noire (autoriser tous les messages entrants exceptés ceux sur la liste noire) - + Use a Whitelist (Block all incoming messages except those on the Whitelist) Utiliser une liste blanche (refuser tous les messages entrants exceptés ceux sur la liste blanche) - + Blacklist Liste noire @@ -192,182 +209,182 @@ p, li { white-space: pre-wrap; } Nombre de connexions - + Total connections: 0 Nombre de connexions total : 0 - + Since startup at asdf: Depuis le lancement à asdf : - + Processed 0 person-to-person message. 0 message de pair à pair traité. - + Processed 0 public key. 0 clé publique traitée. - + Processed 0 broadcast. 0 message de diffusion traité. - + Network Status État du réseau - + File Fichier - + Settings Paramètres - + Help Aide - + Import keys Importer les clés - + Manage keys Gérer les clés - + Quit Quitter - + About À propos - + Regenerate deterministic addresses Regénérer les clés déterministes - + Delete all trashed messages Supprimer tous les messages dans la corbeille - + Total Connections: %1 Nombre total de connexions : %1 - + Not Connected Déconnecté - + Connected Connecté - + Show Bitmessage Afficher Bitmessage - + Subscribe S'abonner - + Processed %1 person-to-person messages. %1 messages de pair à pair traités. - + Processed %1 broadcast messages. %1 messages de diffusion traités. - + Processed %1 public keys. %1 clés publiques traitées. - + Since startup on %1 Depuis lancement le %1 - + Waiting for their encryption key. Will request it again soon. En attente de la clé de chiffrement. Une nouvelle requête sera bientôt lancée. - + Encryption key request queued. Demande de clé de chiffrement en attente. - + Queued. En attente. - + Need to do work to send message. Work is queued. Travail nécessaire pour envoyer le message. Travail en attente. - + Acknowledgement of the message received %1 Accusé de réception reçu le %1 - + Broadcast queued. Message de diffusion en attente. - + Broadcast on %1 Message de diffusion à %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Problème : Le travail demandé par le destinataire est plus difficile que ce que vous avez paramétré. %1 - + Forced difficulty override. Send should start soon. Neutralisation forcée de la difficulté. L'envoi devrait bientôt commencer. - + Message sent. Waiting for acknowledgement. Sent at %1 Message envoyé. En attente de l'accusé de réception. Envoyé le %1 - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. 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. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -376,12 +393,12 @@ It is important that you back up this file. Il est important de faire des sauvegardes de ce fichier. - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) 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.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -390,97 +407,97 @@ It is important that you back up this file. Would you like to open the file now? 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.) - + Add sender to your Address Book Ajouter l'expéditeur au carnet d'adresses - + Move to Trash Envoyer à la Corbeille - + View HTML code as formatted text Voir le code HTML comme du texte formaté - + Enable Activer - + Disable Désactiver - + Copy address to clipboard Copier l'adresse dans le presse-papier - + Special address behavior... Comportement spécial de l'adresse... - + Send message to this address Envoyer un message à cette adresse - + Add New Address Ajouter nouvelle adresse - + Delete Supprimer - + Copy destination address to clipboard Copier l'adresse de destination dans le presse-papier - + Force send Forcer l'envoi - + Are you sure you want to delete all trashed messages? Êtes-vous sûr de vouloir supprimer tous les messages dans la corbeille ? - + You must type your passphrase. If you don't have one then this is not the form for you. Vous devez taper votre phrase secrète. Si vous n'en avez pas, ce formulaire n'est pas pour vous. - + Delete trash? Supprimer la corbeille ? - + Open keys.dat? Ouvrir keys.dat ? - + bad passphrase Mauvaise phrase secrète - + Restart Redémarrer - + You must restart Bitmessage for the port number change to take effect. Vous devez redémarrer Bitmessage pour que le changement de port prenne effet. @@ -490,77 +507,77 @@ Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l'o Bitmessage utilisera votre proxy à partir de maintenant mais il vous faudra redémarrer Bitmessage pour fermer les connexions existantes. - + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. Erreur : Vous ne pouvez pas ajouter une adresse déjà présente dans votre liste. Essayez de renommer l'adresse existante. - + The address you entered was invalid. Ignoring it. L'adresse que vous avez entrée est invalide. Adresse ignorée. - + Passphrase mismatch Phrases secrètes différentes - + The passphrase you entered twice doesn't match. Try again. Les phrases secrètes entrées sont différentes. Réessayez. - + Choose a passphrase Choisissez une phrase secrète - + You really do need a passphrase. Vous devez vraiment utiliser une phrase secrète. - + All done. Closing user interface... Terminé. Fermeture de l'interface... - + Address is gone L'adresse a disparu - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage ne peut pas trouver votre adresse %1. Peut-être l'avez-vous supprimée ? - + Address disabled Adresse désactivée - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. 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. - + Entry added to the Address Book. Edit the label to your liking. Entrée ajoutée au carnet d'adresses. Éditez le label selon votre souhait. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Erreur : Vous ne pouvez pas ajouter une adresse déjà présente dans votre carnet d'adresses. Essayez de renommer l'adresse existante. - + Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. Messages déplacés dans la corbeille. Il n'y a pas d'interface utilisateur pour voir votre corbeille, mais ils sont toujours présents sur le disque si vous souhaitez les récupérer. - + No addresses selected. Aucune adresse sélectionnée. @@ -570,152 +587,152 @@ Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l'o Certaines options ont été désactivées car elles n'étaient pas applicables ou car elles n'ont pas encore été implémentées pour votre système d'exploitation. - + The address should start with ''BM-'' L'adresse devrait commencer avec "BM-" - + The address is not typed or copied correctly (the checksum failed). L'adresse n'est pas correcte (la somme de contrôle a échoué). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Le numéro de version de cette adresse est supérieur à celui que le programme peut supporter. Veuiller mettre Bitmessage à jour. - + The address contains invalid characters. L'adresse contient des caractères invalides. - + Some data encoded in the address is too short. Certaines données encodées dans l'adresse sont trop courtes. - + Some data encoded in the address is too long. Certaines données encodées dans l'adresse sont trop longues. - + Address is valid. L'adresse est valide. - + You are using TCP port %1. (This can be changed in the settings). Vous utilisez le port TCP %1. (Ceci peut être changé dans les paramètres). - + Error: Bitmessage addresses start with BM- Please check %1 Erreur : Les adresses Bitmessage commencent avec BM- Merci de vérifier %1 - + Error: The address %1 contains invalid characters. Please check it. Erreur : L'adresse %1 contient des caractères invalides. Veuillez la vérifier. - + Error: The address %1 is not typed or copied correctly. Please check it. Erreur : L'adresse %1 n'est pas correctement recopiée. Veuillez la vérifier. - + Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Erreur : La version de l'adresse %1 est trop grande. Pensez à mettre à jour Bitmessage. - + Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance. Erreur : Certaines données encodées dans l'adresse %1 sont trop courtes. Il peut y avoir un problème avec le logiciel ou votre connaissance. - + Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance. Erreur : Certaines données encodées dans l'adresse %1 sont trop longues. Il peut y avoir un problème avec le logiciel ou votre connaissance. - + Error: Something is wrong with the address %1. Erreur : Problème avec l'adresse %1. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Erreur : Vous devez spécifier une adresse d'expéditeur. Si vous n'en avez pas, rendez-vous dans l'onglet 'Vos identités'. Sending to your address - Envoi vers votre adresse + Envoi vers votre adresse Error: One of the addresses to which you are sending a message, %1, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM. - Erreur : Une des adresses vers lesquelles vous envoyez un message, %1, est vôtre. Malheureusement, Bitmessage ne peut pas traiter ses propres messages. Essayez de lancer un second client sur une machine différente. + Erreur : Une des adresses vers lesquelles vous envoyez un message, %1, est vôtre. Malheureusement, Bitmessage ne peut pas traiter ses propres messages. Essayez de lancer un second client sur une machine différente. - + Address version number Numéro de version de l'adresse - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. 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. - + Stream number Numéro de flux - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. 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. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. 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. - + Your 'To' field is empty. Votre champ 'Vers' est vide. - + Right click one or more entries in your address book and select 'Send message to this address'. Cliquez droit sur une ou plusieurs entrées dans votre carnet d'adresses et sélectionnez 'Envoyer un message à ces adresses'. - + Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want. Erreur : Vous ne pouvez pas ajouter une même adresse à vos abonnements deux fois. Essayez de renommer l'adresse existante. - + Message trashed Message envoyé à la corbeille - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? 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 ? - + Unknown status: %1 %2 Statut inconnu : %1 %2 - + Connection lost Connexion perdue @@ -725,9 +742,8 @@ Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l'o Problème d'authentification SOCKS5 : %1 - + Reply - . Répondre @@ -790,10 +806,10 @@ Difficulté requise par le destinataire : %1 et %2 Work is queued. - Travail en attente. + Travail en attente. - + Work is queued. %1 Travail en attente. %1 @@ -805,185 +821,279 @@ There is no required difficulty for version 2 addresses like this. Il n'y a pas de difficulté requise pour ces adresses de version 2. - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 - + Save message as... - + Mark Unread - + Subscribe to this address - + Message sent. Sent at %1 - + Chan name needed - + You didn't enter a chan name. - + Address already present - + Could not add chan because it appears to already be one of your identities. - + Success - + Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'. - + Address too new - + Although that Bitmessage address might be valid, its version number is too new for us to handle. Perhaps you need to upgrade Bitmessage. - + Address invalid - + That Bitmessage address is not valid. - + Address does not match chan name - + Although the Bitmessage address you entered was valid, it doesn't match the chan name. - + Successfully joined chan. - + Fetched address from namecoin identity. - + New Message - + From - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). - + Save As... - + Write error. - - Options have been disabled because they either aren't applicable or because they haven't yet been implemented for your operating system. - - - - + Testing... - + This is a chan address. You cannot use it as a pseudo-mailing list. - + Search - + All - + Message - + Fetch Namecoin ID - + Stream # - + Connections - + Ctrl+Q Ctrl+Q - + F1 - + Join / Create chan + + + Set avatar... + + + + + Bad address version number + + + + + Your address version number must be a number: either 3 or 4. + + + + + Your address version number must be either 3 or 4. + + + + + Inventory lookups per second: %1 + + + + + Will not resend ever + + + + + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. + + + + + Do you really want to remove this avatar? + + + + + You have already set an avatar for this address. Do you really want to overwrite it? + + + + + Start-on-login not yet supported on your OS. + + + + + Minimize-to-tray not yet supported on your OS. + + + + + Tray notifications not yet supported on your OS. + + + + + Enter an address above. + + + + + Address is an old type. We cannot display its past broadcasts. + + + + + There are no recent broadcasts from this address to display. + + + + + Display the %1 recent broadcast from this address. + + + + + Display the %1 recent broadcasts from this address. + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + + + + + Inventory lookups per second: 0 + + MainWindows @@ -1035,7 +1145,7 @@ L'option 'Nombre Aléatoire' est sélectionnée par défaut mais Address version number: 3 - Numéro de version de l'adresse : 3 + Numéro de version de l'adresse : 3 @@ -1092,24 +1202,34 @@ L'option 'Nombre Aléatoire' est sélectionnée par défaut mais (saves you some bandwidth and processing power) (économise de la bande passante et de la puissance de calcul) + + + Address version number: 4 + Numéro de version de l'adresse : 4 + NewSubscriptionDialog - + Add new entry Ajouter une nouvelle entrée - + Label Label - + Address Adresse + + + CheckBox + + SpecialAddressBehaviorDialog @@ -1142,35 +1262,40 @@ L'option 'Nombre Aléatoire' est sélectionnée par défaut mais aboutDialog - + PyBitmessage PyBitmessage - + version ? version ? - + About À propos - + Copyright © 2013 Jonathan Warren - Copyright © 2013 Jonathan Warren + Copyright © 2013 Jonathan Warren - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <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> <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> - + This is Beta software. Version bêta. + + + <html><head/><body><p>Copyright © 2012-2013 Jonathan Warren<br/>Copyright © 2013 The Bitmessage Developers</p></body></html> + + connectDialog @@ -1287,272 +1412,372 @@ L'option 'Nombre Aléatoire' est sélectionnée par défaut mais regenerateAddressesDialog - + Regenerate Existing Addresses Regénérer des adresses existantes - + Regenerate existing addresses Regénérer des adresses existantes - + Passphrase Phrase secrète - + Number of addresses to make based on your passphrase: Nombre d'adresses basées sur votre phrase secrète à créer : Address version Number: - Numéro de version de l'adresse : + Numéro de version de l'adresse : 3 - 3 + 3 - + Stream number: Numéro du flux : - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Créer une adresse plus courte d'un ou deux caractères (nécessite plusieurs minutes de temps de calcul supplémentaires) - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. 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. - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. Si vous aviez généré des adresses déterministes mais les avez perdues à cause d'un accident, vous pouvez les regé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é. + + + Address version number: + + settingsDialog - + Settings Paramètres - + Start Bitmessage on user login Démarrer Bitmessage à la connexion de l'utilisateur - + Start Bitmessage in the tray (don't show main window) Démarrer Bitmessage dans la barre des tâches (ne pas montrer la fenêtre principale) - + Minimize to tray Minimiser dans la barre des tâches - + Show notification when message received Montrer une notification lorsqu'un message est reçu - + Run in Portable Mode Lancer en Mode Portable - + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. En Mode Portable, les messages et les fichiers de configuration sont stockés dans le même dossier que le programme plutôt que le dossier de l'application. Cela rend l'utilisation de Bitmessage plus facile depuis une clé USB. - + User Interface Interface utilisateur - + Listening port Port d'écoute - + Listen for connections on port: Écouter les connexions sur le port : - + Proxy server / Tor Serveur proxy / Tor - + Type: Type : - + none aucun - + SOCKS4a SOCKS4a - + SOCKS5 SOCKS5 - + Server hostname: Nom du serveur : - + Port: Port : - + Authentication Authentification - + Username: Utilisateur : - + Pass: Mot de passe : - + Network Settings Paramètres réseau - + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. Lorsque quelqu'un vous envoie un message, son ordinateur doit d'abord effectuer un travail. La difficulté de ce travail, par défaut, est de 1. Vous pouvez augmenter cette valeur pour les adresses que vous créez en changeant la valeur ici. Chaque nouvelle adresse que vous créez requerra à l'envoyeur de faire face à une difficulté supérieure. Il existe une exception : si vous ajoutez un ami ou une connaissance à votre carnet d'adresses, Bitmessage les notifiera automatiquement lors du prochain message que vous leur envoyez qu'ils ne doivent compléter que la charge de travail minimale : difficulté 1. - + Total difficulty: Difficulté totale : - + Small message difficulty: Difficulté d'un message court : - + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. La 'difficulté d'un message court' affecte principalement la difficulté d'envoyer des messages courts. Doubler cette valeur rend la difficulté à envoyer un court message presque double, tandis qu'un message plus long ne sera pas réellement affecté. - + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. La 'difficulté totale' affecte le montant total de travail que l'envoyeur devra compléter. Doubler cette valeur double la charge de travail. - + Demanded difficulty Difficulté demandée - + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. Vous pouvez préciser quelle charge de travail vous êtes prêt à effectuer afin d'envoyer un message à une personne. Placer cette valeur à 0 signifie que n'importe quelle valeur est acceptée. - + Maximum acceptable total difficulty: Difficulté maximale acceptée : - + Maximum acceptable small message difficulty: Difficulté maximale pour les messages courts acceptée : - + Max acceptable difficulty Difficulté acceptée max - + Willingly include unencrypted destination address when sending to a mobile device - - Override automatic language localization (use countycode or language code, e.g. 'en_US' or 'en'): - - - - + Listen for incoming connections when using proxy - + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> - + Host: - + Password: - + Test - + Connect to: - + Namecoind - + NMControl - + Namecoin integration + + + Use Identicons + + + + + Interface Language + + + + + System Settings + system + + + + + English + en + + + + + Esperanto + eo + + + + + Français + fr + + + + + Deutsch + de + + + + + Españl + es + + + + + русский язык + ru + + + + + Norsk + no + + + + + Pirate English + en_pirate + + + + + Other (set in keys.dat) + other + + + + + <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> + + + + + Give up after + + + + + and + + + + + days + + + + + months. + + + + + Resends Expire + + diff --git a/src/translations/bitmessage_no.pro b/src/translations/bitmessage_no.pro new file mode 100644 index 00000000..d34ad2f9 --- /dev/null +++ b/src/translations/bitmessage_no.pro @@ -0,0 +1,35 @@ +SOURCES = ../addresses.py\ + ../bitmessagemain.py\ + ../class_addressGenerator.py\ + ../class_objectProcessor.py\ + ../class_outgoingSynSender.py\ + ../class_receiveDataThread.py\ + ../class_sendDataThread.py\ + ../class_singleCleaner.py\ + ../class_singleListener.py\ + ../class_singleWorker.py\ + ../class_sqlThread.py\ + ../helper_bitcoin.py\ + ../helper_bootstrap.py\ + ../helper_generic.py\ + ../helper_inbox.py\ + ../helper_sent.py\ + ../helper_startup.py\ + ../shared.py\ + ../bitmessageqt/__init__.py\ + ../bitmessageqt/about.py\ + ../bitmessageqt/addaddressdialog.py\ + ../bitmessageqt/bitmessageui.py\ + ../bitmessageqt/connect.py\ + ../bitmessageqt/help.py\ + ../bitmessageqt/iconglossary.py\ + ../bitmessageqt/newaddressdialog.py\ + ../bitmessageqt/newchandialog.py\ + ../bitmessageqt/newsubscriptiondialog.py\ + ../bitmessageqt/regenerateaddresses.py\ + ../bitmessageqt/settings.py\ + ../bitmessageqt/specialaddressbehavior.py + + +TRANSLATIONS = bitmessage_no.ts +CODECFORTR = UTF-8 diff --git a/src/translations/bitmessage_no.qm b/src/translations/bitmessage_no.qm new file mode 100644 index 00000000..e0b248c6 Binary files /dev/null and b/src/translations/bitmessage_no.qm differ diff --git a/src/translations/bitmessage_no.ts b/src/translations/bitmessage_no.ts new file mode 100644 index 00000000..285002a1 --- /dev/null +++ b/src/translations/bitmessage_no.ts @@ -0,0 +1,1806 @@ + + + + AddAddressDialog + + + Add new entry + Legg til ny oppføring + + + + Label + Etikett + + + + Address + Adresse + + + + MainWindow + + + Bitmessage + Bitmessage + + + + To + Til + + + + From + Fra + + + + Subject + Emne + + + + Received + Mottatt + + + + Inbox + Innboks + + + + Load from Address book + Velg fra adresseboka + + + + Message: + Beskjed: + + + + Subject: + Emne: + + + + Send to one or more specific people + Send til en eller flere bestemte kontakter + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + To: + Til: + + + + From: + Fra: + + + + Broadcast to everyone who is subscribed to your address + Kringkast til alle som abonnerer på din adresse + + + + Send + Send + + + + Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. + Vær klar over at når du kringkaster noe er beskjeden kun kryptert med adressen din. Alle som har denne kan derfor få med seg innholdet. + + + + Status + Status + + + + Sent + Sendt + + + + New + Ny + + + + Label (not shown to anyone) + Etikett (ikke vist til noen) + + + + Address + Adresse + + + + Stream + Strøm + + + + Your Identities + Dine identiteter + + + + Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. + Her kan du abonnere på 'kringkastede beskjeder' sendt av andre brukere. Beskjedene vil vises i din innboks. Adressene her vil overstyre de under svartelistefanen. + + + + Add new Subscription + Legg til nytt abonnement + + + + Label + Etikett + + + + Subscriptions + Abonnement + + + + The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. + Adresseboka er nyttig for å knytte navn eller etiketter mot andres BitMessage-adresser så du enklere kan gjenkjenne dem i innboksen. Du kan legge til nye oppføringer her ved å bruke 'Legg til'-knappen, eller fra innboksen din ved å høyreklikke på en beskjed. + + + + Add new entry + Legg til ny oppføring + + + + Name or Label + Navn eller etikett + + + + Address Book + Adressebok + + + + Use a Blacklist (Allow all incoming messages except those on the Blacklist) + Bruk svarteliste (tillat beskjeder fra alle adresser unntatt de på svartelisten) + + + + Use a Whitelist (Block all incoming messages except those on the Whitelist) + Bruk hviteliste (blokker beskjeder fra alle adresser unntatt de på hvitelisten) + + + + Blacklist + Svarteliste + + + + Stream Number + Strømnummer + + + + Number of Connections + Antall tilkoblinger + + + + Total connections: 0 + Totalt antall tilkoblinger: 0 + + + + Since startup at asdf: + Siden oppstart på asdf: + + + + Processed 0 person-to-person message. + Har bearbeidet 0 beskjeder for person-til-person. + + + + Processed 0 public key. + Har bearbeidet 0 offentlige nøkler. + + + + Processed 0 broadcast. + Har bearbeidet 0 kringkastninger. + + + + Network Status + Nettverksstatus + + + + File + Fil + + + + Settings + Innstillinger + + + + Help + Hjelp + + + + Import keys + Importer inn nøkler + + + + Manage keys + Administrer nøkler + + + + Quit + Avslutt + + + + About + Om + + + + Regenerate deterministic addresses + Regenerer deterministiske adresser + + + + Delete all trashed messages + Slett alle kastede meldinger + + + + Total Connections: %1 + Totalt antall forbindelser: %1 + + + + Not Connected + Ikke tilkoblet + + + + Connected + Tilkoblet + + + + Show Bitmessage + Vis Bitmessage + + + + Subscribe + Abonner + + + + Processed %1 person-to-person messages. + Bearbeidet %1 beskjeder for person-til-person. + + + + Processed %1 broadcast messages. + Bearbeidet %1 kringkastede beskjeder. + + + + Processed %1 public keys. + Bearbeidet %1 offentlige nøkler. + + + + Since startup on %1 + Siden oppstart %1 + + + + Waiting for their encryption key. Will request it again soon. + Venter på krypteringsnøkkel. Sender straks en ny forespørsel. + + + + Encryption key request queued. + Forespørsel for å finne krypteringsnøkkel er satt i kø. + + + + Queued. + Satt i kø. + + + + Need to do work to send message. Work is queued. + Trenger å utføre arbeidsoppgave for sende beskjed. Denne er satt i kø. + + + + Acknowledgement of the message received %1 + Bekreftelse på beskjeden mottatt %1 + + + + Broadcast queued. + Kringkasting satt i kø. + + + + Broadcast on %1 + Kringkasting på %1 + + + + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 + Problem: Det nødvendige arbeidet som kreves utført av mottaker er mer krevende enn det som er satt som akseptabelt. %1 + + + + Forced difficulty override. Send should start soon. + Tvunget vanskelighet overstyrt. Sender snart. + + + + Message sent. Waiting for acknowledgement. Sent at %1 + Beskjed sendt. Venter på bekreftelse. Sendt %1 + + + + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. + Du kan administrere nøklene dine ved å endre filen keys.dat i samme katalog som dette programmet. Det er viktig at du tar en sikkerhetskopi av denne filen. + + + + You may manage your keys by editing the keys.dat file stored in + %1 +It is important that you back up this file. + Du kan administrere nøklene dine ved å endre filen keys.dat lagret i + %1 +Det er viktig at du tar en sikkerhetskopi av denne filen. + + + + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) + Du kan administrere nøklene dine ved å endre filen keys.dat i samme katalog som dette programmet. Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne filen nå? (Pass på å lukke Bitmessage før du gjør endringer.) + + + + You may manage your keys by editing the keys.dat file stored in + %1 +It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) + Du kan administrere nøklene dine ved å endre filen keys.dat i + %1 +Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne filen nå? (Pass på å lukke Bitmessage før du gjør endringer.) + + + + Add sender to your Address Book + Legg til sender i adresseboka + + + + Move to Trash + Kast + + + + View HTML code as formatted text + Vis HTML-koden som formatert tekst + + + + Enable + Aktiver + + + + Disable + Deaktiver + + + + Copy address to clipboard + Kopier adressen til utklippstavlen + + + + Special address behavior... + Spesieladressebehandling ... + + + + Send message to this address + Send beskjed til denne adressen + + + + Add New Address + Legg til ny adresse + + + + Delete + Slett + + + + Copy destination address to clipboard + Kopier destinasjonsadresse til utklippstavlen + + + + Force send + Tving sending + + + + Are you sure you want to delete all trashed messages? + Er du sikker på at du vil slette alle kastede beskjeder? + + + + You must type your passphrase. If you don't have one then this is not the form for you. + Du må skrive inn passordfrasen din. Hvis du ikke har en kan du ikke bruke dette skjemaet. + + + + Delete trash? + Vil du slette kastet innhold? + + + + Open keys.dat? + Åpne keys.dat? + + + + bad passphrase + Dårlig passordfrase + + + + Restart + Omstart + + + + You must restart Bitmessage for the port number change to take effect. + Du må ta omstart av Bitmessage for at endringen av portnummer skal tre i kraft. + + + + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections. + Bitmessage vil bruke proxy fra nå av, ta en omstart hvis du vil lukke alle eksisterende tilkoblinger. + + + + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. + Feil: Du kan ikke legge til samme adresse flere ganger. + + + + The address you entered was invalid. Ignoring it. + Adressen du oppga var ugyldig og vil derfor bli ignorert. + + + + Passphrase mismatch + Passordfrase stemmer ikke + + + + The passphrase you entered twice doesn't match. Try again. + Passordfrasene er ikke like. Vennligst prøv igjen. + + + + Choose a passphrase + Velg en passordfrase + + + + You really do need a passphrase. + Du trenger sårt en passordfrase. + + + + All done. Closing user interface... + Ferdig. Lukker brukergrensesnittet... + + + + Address is gone + Adressen er borte + + + + Bitmessage cannot find your address %1. Perhaps you removed it? + Bitmessage kan ikke finne adressen %1. Kanskje du fjernet den? + + + + Address disabled + Adressen er deaktivert + + + + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. + Feil: Adressen du prøver å sende med er deaktivert. Du må aktivere den fra 'Dine identiteter' før du kan bruke den. + + + + Entry added to the Address Book. Edit the label to your liking. + Ny oppføring lagt til i adresseboka. Du kan forandre etiketten til det du måtte ønske. + + + + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. + Feil: Du kan ikke legge til samme adresse i adresseboka flere ganger. + + + + Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. + Kastet innholdet. Det finnes ikke noe brukergrensesnitt enda for kastet innhold, men ingenting er slettet enda. Alt ligger fortsatt på disken hvis du er interessert i å få det tilbake. + + + + No addresses selected. + Ingen adresse valgt. + + + + Options have been disabled because they either aren't applicable or because they haven't yet been implimented for your operating system. + Alternativer har blitt deaktivert fordi de enten ikke er gjeldende eller fordi de ikke har blitt implementert for ditt operativsystem. + + + + The address should start with ''BM-'' + Adressen bør starte med ''BM-'' + + + + The address is not typed or copied correctly (the checksum failed). + Adressen er ikke skrevet eller kopiert inn riktig (sjekksummen feilet). + + + + The version number of this address is higher than this software can support. Please upgrade Bitmessage. + Typenummeret for denne adressen er høyere enn det programvaren støtter. Vennligst oppgrader Bitmessage. + + + + The address contains invalid characters. + Adressen inneholder ugyldige tegn. + + + + Some data encoded in the address is too short. + Noen av de kodede dataene i adressen er for korte. + + + + Some data encoded in the address is too long. + Noen av de kodede dataene i adressen er for lange. + + + + Address is valid. + Adressen er gyldig. + + + + You are using TCP port %1. (This can be changed in the settings). + Du benytter TCP-port %1. (Dette kan endres på i innstillingene). + + + + Error: Bitmessage addresses start with BM- Please check %1 + Feil: Bitmessage-adresser begynner med BM-. Vennligst sjekk %1 + + + + Error: The address %1 contains invalid characters. Please check it. + Feil: Adressen %1 innerholder ugyldige tegn. Vennligst sjekk den. + + + + Error: The address %1 is not typed or copied correctly. Please check it. + Feil: Adressen %1 er skrevet eller kopiert inn feil. Vennligst sjekk den. + + + + Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. + Feil: Typenummeret for adressen %1 er for høy. Enten trenger du å oppgradere Bitmessaage-programvaren eller så er det fordi kontakten din har funnet på noe smart. + + + + Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance. + Feil: Noen av de kodede dataene i adressen %1 er for korte. Det kan hende det er noe galt med programvaren til kontakten din. + + + + Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance. + Feil: Noen av de kodede dataene i adressen %1 er for lange. Det kan hende det er noe galt med programvaren til kontakten din. + + + + Error: Something is wrong with the address %1. + Feil: Noe er galt med adressen %1. + + + + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. + Feil: Du må oppgi en avsenderadresse. Hvis du ikke har en gå til 'Dine identiteter'-fanen. + + + + Sending to your address + Sender til din adresse + + + + Error: One of the addresses to which you are sending a message, %1, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM. + Feil: En av adressene du sender en beskjed til er dine: %1. Dessverre kan ikke Bitmessage-klienten bearbeide sine egne beskjeder. Du kan benytte Bitmessage-klienten på en annen datamaskin eller kjøre den i en virtuell datamaskin. + + + + Address version number + Adressetypenummer + + + + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. + Angående adressen %1, Bitmessage forstår ikke adressetypenumre for %2. Oppdater Bitmessage til siste versjon. + + + + Stream number + Strømnummer + + + + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. + Angående adressen %1, Bitmessage kan ikke håndtere strømnumre for %2. Oppdater Bitmessage til siste utgivelse. + + + + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. + Advarsel: Du er ikke tilkoblet. Bitmessage vil utføre nødvendige arbeidsoppgaver for å sende beskjeder, men ingen vil bli sendt før du kobler til igjen. + + + + Your 'To' field is empty. + Ditt 'Til'-felt er tomt. + + + + Right click one or more entries in your address book and select 'Send message to this address'. + Høyreklikk på en eller flere oppføringer i adresseboka og velg 'Send beskjed til denne adressen'. + + + + Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want. + Feil: Du kan ikke legge til samme adresse flere ganger i abonnementlista. + + + + Message trashed + Beskjed kastet + + + + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? + En av dine gamle adresser er av den første typen og derfor ikke lenger støttet: %1. Derfor kan den vel slettes? + + + + Unknown status: %1 %2 + Ukjent status: %1 %2 + + + + Connection lost + Mistet tilkobling + + + + SOCKS5 Authentication problem: %1 + Autentiseringsproblem med SOCKS5: %1 + + + + Reply + Svar + + + + Generating one new address + Genererer en ny adresse + + + + Done generating address. Doing work necessary to broadcast it... + Ferdig med å generere adresse. Utfører nødvendig arbeidsoppgave for å kringkaste den ... + + + + Done generating address + Ferdig med å generere adresse + + + + Message sent. Waiting on acknowledgement. Sent on %1 + Beskjed sendt, venter på bekreftelse. Sendt %1 + + + + Error! Could not find sender address (your address) in the keys.dat file. + Feil! Kunne ikke finne avsenderadresse (din adresse) i nøkkelfilen som er keys.dat. + + + + Doing work necessary to send broadcast... + Utfører nødvendig arbeidsoppgave for å kringkaste ... + + + + Broadcast sent on %1 + Kringkastet på %1 + + + + Looking up the receiver's public key + Gjør oppslag for å finne mottakers offentlige nøkkel + + + + Doing work necessary to send message. (There is no required difficulty for version 2 addresses like this.) + Utfører nødvendig arbeidsoppgave for å sende beskjeden. (Det er ikke noe krav til vanskelighet for adresser av type to som benyttet her.) + + + + Doing work necessary to send message. +Receiver's required difficulty: %1 and %2 + Utfører nødvendig arbeidsoppgave for å sende beskjeden. +Mottakernes krav til vanskelighet: %1 og %2 + + + + Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. + Problem: Arbeidsoppgaven som kreves utført av mottaker (%1 og %2) er mer krevende enn det du har satt som akseptabelt. + + + + Work is queued. + Arbeidsoppgave er satt i kø. + + + + Work is queued. %1 + Arbeidsoppgave er satt i kø. %1 + + + + Doing work necessary to send message. +There is no required difficulty for version 2 addresses like this. + Utfører nødvendig arbeidsoppgave for å sende beskjeden. +Det er ingen krevd vanskelighet for adresser av type to som benyttet her. + + + + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 + Problem: Mottakerens nøkkel kunne ikke brukes til å kryptere beskjeden. %1 + + + + Save message as... + Lagre beskjed som ... + + + + Mark Unread + Merk som ulest + + + + Subscribe to this address + Abonner på denne adressen + + + + Message sent. Sent at %1 + Beskjed sendt. Sendt %1 + + + + Chan name needed + Kanalnavn nødvendig + + + + You didn't enter a chan name. + Du oppga ikke noe kanalnavn. + + + + Address already present + Adressen eksisterer allerede + + + + Could not add chan because it appears to already be one of your identities. + Kunne ikke legge til kanal siden den ser ut til å allerede være lagret som en av dine identiteter. + + + + Success + Suksess + + + + Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'. + Opprettet ny kanal. For å la andre delta i din nye kanal gir du dem dem kanalnavnet og denne Bitmessage-adressen: %1. Denne adressen vises også i 'Dine identiteter'. + + + + Address too new + Adressen er for ny + + + + Although that Bitmessage address might be valid, its version number is too new for us to handle. Perhaps you need to upgrade Bitmessage. + Selv om Bitmessage-adressen kanskje er gyldig så er tilhørende typenummer for nytt til å håndteres. Kanskje du trenger å oppgradere Bitmessage. + + + + Address invalid + Ugyldig adresse + + + + That Bitmessage address is not valid. + Bitmessage-adressen er ikke gyldig. + + + + Address does not match chan name + Adresse stemmer ikke med kanalnavnet + + + + Although the Bitmessage address you entered was valid, it doesn't match the chan name. + Selv om Bitmessage-adressen du oppga var gyldig stemmer den ikke med kanalnavnet. + + + + Successfully joined chan. + Deltar nå i kanal. + + + + Fetched address from namecoin identity. + Hentet adresse fra Namecoin-identitet. + + + + New Message + Ny beskjed + + + + From + Fra + + + + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). + Bitmessage vil bruke proxy fra nå av. Hvis du vil kan du omstart av programmet for å lukke eksisterende tilkoblinger (hvis det finnes noen). + + + + Save As... + Lagre som ... + + + + Write error. + Skrivefeil. + + + + Options have been disabled because they either aren't applicable or because they haven't yet been implemented for your operating system. + Alternativer har blitt deaktivert fordi de enten ikke er gjeldende eller fordi de ikke har blitt implementert for ditt operativsystem. + + + + Testing... + Tester ... + + + + This is a chan address. You cannot use it as a pseudo-mailing list. + Dette er en kanaladresse. Du kan ikke bruke den som en pseudo-epostliste. + + + + Search + Søk + + + + All + Alle + + + + Message + Beskjed + + + + Fetch Namecoin ID + Hent Namecoin-id + + + + Stream # + Strøm # + + + + Connections + Tilkoblinger + + + + Ctrl+Q + Ctrl+Q + + + + F1 + F1 + + + + Join / Create chan + Delta i / opprett kanal + + + + Set avatar... + Sett ett avatar... + + + + You may manage your keys by editing the keys.dat file stored in + %1 +It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) + Du kan administrere dine nøkler ved å endre på filen keys.dat lagret i + %1 +Det er viktig at du tar sikkerhetskopi av denne filen. Vil du åpne denne filen nå? (Vær sikker på å få avsluttet Bitmessage før du gjør endringer.) + + + + Bad address version number + Feil adresseversjonsnummer + + + + Your address version number must be a number: either 3 or 4. + Ditt adressetypenummer må være et nummer: Enten 3 eller 4. + + + + Your address version number must be either 3 or 4. + Ditt adressetypenummer må enten være 3 eller 4. + + + + Inventory lookups per second: %1 + Inventaroppslag per sekund: %1 + + + + Will not resend ever + Vil ikke igjensende noensinne + + + + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. + Legg merke til at utløpstiden du oppga er kortere enn det Bitmessage venter for første igjensendingsforsøk, dine beskjeder vil derfor aldri bli igjensendt. + + + + Do you really want to remove this avatar? + Vil du virkelig fjerne dette avataret? + + + + You have already set an avatar for this address. Do you really want to overwrite it? + Du har allerede satt ett avatar for denne adressen. Vil du virkelig overskrive det? + + + + Start-on-login not yet supported on your OS. + Start ved innlogging er ikke støttet enda for ditt OS. + + + + Minimize-to-tray not yet supported on your OS. + Minimering til systemstatusfeltet er ikke støttet enda for ditt OS. + + + + Tray notifications not yet supported on your OS. + Varslinger via systemstatusfeltet er ikke støttet enda for ditt OS. + + + + Enter an address above. + Oppgi inn en adresse over. + + + + Address is an old type. We cannot display its past broadcasts. + Adressen er av gammel type. Vi kan ikke vise dens tidligere kringkastninger. + + + + There are no recent broadcasts from this address to display. + Det er ingen nylige kringkastninger fra denne adressen å vise frem. + + + + Display the %1 recent broadcast from this address. + Vis den %1 nyligste kringkastningen fra denne adressen. + + + + Display the %1 recent broadcasts from this address. + Vis de %1 nyligste kringkastningene fra denne adressen. + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + + + + Inventory lookups per second: 0 + Inventaroppslag per sekund: 0 + + + + MainWindows + + + Address is valid. + Adressen er gyldig. + + + + NewAddressDialog + + + Create new Address + Opprett ny adresse + + + + Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a "deterministic" address. +The 'Random Number' option is selected by default but deterministic addresses have several pros and cons: + Her kan du generere så mange adresser du vil. Du oppfordres til å ta i bruk nye adresser med jevne mellomrom. Du kan generere nye adresser enten ved å bruke tilfeldige numre eller en passordfrase. Hvis du bruker passordfrase får du en såkalt 'deterministisk' adresse. +'Tilfeldig nummer'-valget er valgt som standard. En deterministisk adresse har både fordeler og ulemper: + + + + <html><head/><body><p><span style=" font-weight:600;">Pros:<br/></span>You can recreate your addresses on any computer from memory. <br/>You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. <br/><span style=" font-weight:600;">Cons:<br/></span>You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost. <br/>You must remember the address version number and the stream number along with your passphrase. <br/>If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.</p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Fordeler:<br/></span>Du kan gjenskape adressene dine på hvilken som helst datamaskin ved hjelp av hukommelsen. <br/>Du trenger ikke ta noen sikkerhetskopi av keys.dat-filen så lenge du husker passordfrasen din. <br/><span style=" font-weight:600;">Ulemper:<br/></span>Du må huske (eller skrive ned) din passordfrase hvis du forventer å måtte gjenopprette nøklene dine fordi de går tapt. <br/>Du må huske adresseversjonsnummeret og strømnummeret i tillegg til passordfrasen. <br/>Hvis du velger en svak passordfrase og noen andre på Internett klarer å knekke den kan de lese beskjedene dine og sende nye beskjeder på vegne av deg.</p></body></html> + + + + Use a random number generator to make an address + Opprett en adresse ved å bruke generatoren som lager tilfeldige tall + + + + Use a passphrase to make addresses + Bruk en passordfrase for å opprette adresser + + + + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter + Bruk ekstra tid på å få adressen(e) en eller to tegn kortere + + + + Make deterministic addresses + Opprett deterministisk adresse + + + + Address version number: 3 + Adressetypenummer: 3 + + + + In addition to your passphrase, you must remember these numbers: + I tillegg til passordfrasen må du huske disse numrene: + + + + Passphrase + Passordfrase + + + + Number of addresses to make based on your passphrase: + Antall adresser som skal opprettes basert på din passordfrase: + + + + Stream number: 1 + Strømnummer: 1 + + + + Retype passphrase + Gjenta passordfrase + + + + Randomly generate address + Generer tilfeldig adresse + + + + Label (not shown to anyone except you) + Etikett (ikke vist til noen andre enn deg) + + + + Use the most available stream + Bruk den mest tilgjengelige strømmen + + + + (best if this is the first of many addresses you will create) + (best hvis dette er de første av mange adresser du kommer til å opprette) + + + + Use the same stream as an existing address + Bruk samme strøm som en eksisterende adresse + + + + (saves you some bandwidth and processing power) + (sparer deg for litt båndbredde og prosesseringskraft) + + + + Address version number: 4 + Adressetypenummer: 4 + + + + NewSubscriptionDialog + + + Add new entry + Legg til ny oppføring + + + + Label + Etikett + + + + Address + Adresse + + + + CheckBox + Avkryssningsboks + + + + SpecialAddressBehaviorDialog + + + Special Address Behavior + Spesiell adresseoppførsel + + + + Behave as a normal address + Oppførsel som vanlig adresse + + + + Behave as a pseudo-mailing-list address + Oppførsel som adresse på pseudo-epostliste + + + + Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public). + E-post mottatt med en adresse oppført på en pseudo-epostliste vil automatisk bli kringkastet til abonnenter (og vil derfor bli offentlig tilgjengelig). + + + + Name of the pseudo-mailing-list: + Navnet på pseudo-epostlista: + + + + aboutDialog + + + PyBitmessage + PyBitmessage + + + + version ? + versjon ? + + + + About + Om + + + + Copyright © 2013 Jonathan Warren + Kopirett © 2013 Jonathan Warren + + + + <html><head/><body><p>Distributed under the MIT/X11 software license; see <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> + <html><head/><body><p>Distribuert under MIT/X11-programvarelisens, se <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> + + + + This is Beta software. + Dette er betaprogramvare. + + + + <html><head/><body><p>Copyright © 2012-2014 Jonathan Warren<br/>Copyright © 2013-2014 The Bitmessage Developers</p></body></html> + <html><head/><body><p>Kopirett © 2012-2014 Jonathan Warren<br/>Kopirett © 2013-2014 Bitmessage-utviklerne</p></body></html> + + + + connectDialog + + + Bitmessage + Bitmessage + + + + Bitmessage won't connect to anyone until you let it. + Bitmessage kobler ikke til noen før du lar den. + + + + Connect now + Koble til nå + + + + Let me configure special network settings first + La meg konfigurere spesielle nettverksinnstillinger først + + + + helpDialog + + + Help + Hjelp + + + + <a href="http://Bitmessage.org/wiki/PyBitmessage_Help">http://Bitmessage.org/wiki/PyBitmessage_Help</a> + <a href="http://Bitmessage.org/wiki/PyBitmessage_Help">PyBitmessage-hjelp</a> + + + + As Bitmessage is a collaborative project, help can be found online in the Bitmessage Wiki: + Bitmessage er et samarbeidsprosjekt, hjelp kan bli funnet på nettet i Bitmessage-wikien: + + + + iconGlossaryDialog + + + Icon Glossary + Statusoversikt + + + + You have no connections with other peers. + Du har ingen tilkoblinger til andre. + + + + You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn't configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node. + Du har opprettet minst en utgående tilkobling til andre, men ikke mottatt noen innkommende tilkoblinger enda. Din brannmur eller ruter er antagelig ikke konfigurert til å videreformidle innkommende TCP-tilkoblinger frem til datamaskinen din. Bitmessage vil fungere helt fint, men det ville hjelpe Bitmessage-nettverket hvis du tillot innkommende tilkoblinger. Det vil også hjelpe deg å bli en bedre tilkoblet node. + + + + You are using TCP port ?. (This can be changed in the settings). + Du bruker TCP port ?. (Dette kan endres på fra innstillingene). + + + + You do have connections with other peers and your firewall is correctly configured. + Du har aktive tilkoblinger til andre og riktig konfigurert brannmur. + + + + newChanDialog + + + Dialog + Opprett ny kanal + + + + Create a new chan + Opprett en ny kanal + + + + Join a chan + Delta i kanal + + + + Create a chan + Opprett en kanal + + + + <html><head/><body><p>Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. If you and someone else both create a chan with the same chan name then it is currently very likely that they will be the same chan.</p></body></html> + <html><head/><body><p>Skriv inn et navn for kanalen din. Hvis du velger et komplisert nok kanalnavn (et som er langt nok og unikt som passfrase) og ingen av dine kontakter deler det offentlig vil kanalen være sikker og privat. Hvis du og noen andre begge oppretter en kanal med samme kanalnavnet vil dette bli samme kanalen</p></body></html> + + + + Chan name: + Kanalnavn: + + + + <html><head/><body><p>A chan exists when a group of people share the same decryption keys. The keys and bitmessage address used by a chan are generated from a human-friendly word or phrase (the chan name). To send a message to everyone in the chan, send a normal person-to-person message to the chan address.</p><p>Chans are experimental and completely unmoderatable.</p></body></html> + <html><head/><body><p>En kanal eksisterer når en gruppe personer deler de samme dekrypteringsnøklene. Nøklene og Bitmessage-adressen brukt av kanalen er generert fra et menneskevennlig ord eller en frase (kanalnavnet). For å sende en beskjed til alle som er i kanalen sender man en vanlig beskjed av typen person-til-person til kanaladressen.</p><p>Kanaler er fullstendig umodererbare og eksperimentelle.</p></body></html> + + + + Chan bitmessage address: + Bitmessage-kanaladresse: + + + + regenerateAddressesDialog + + + Regenerate Existing Addresses + Regenerer eksisterende adresser + + + + Regenerate existing addresses + Regenerer eksisterende adresser + + + + Passphrase + Passordfrase + + + + Number of addresses to make based on your passphrase: + Antall adresser som skal opprettes basert på din passordfrase: + + + + Address version Number: + Adressetypenummer: + + + + 3 + 3 + + + + Stream number: + Strømnummer: + + + + 1 + 1 + + + + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter + Bruk ekstra tid på å få adressen(e) en eller to tegn kortere + + + + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. + Du må krysse av for (eller ikke krysse av for) i denne boksen slik du gjorde når du opprettet adressene dine første gangen. + + + + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. + Hvis du tidligere har opprettet deterministiske adresser, men mistet dem p.g.a. et uhell (f.eks. harddiskkræsj) så kan de regenereres her. Hvis du derimot brukte generatoren for generering av tilfeldige tall vil ikke dette skjemaet være til hjelp for deg. + + + + Address version number: + Adressetypenummer: + + + + settingsDialog + + + Settings + Innstillinger + + + + Start Bitmessage on user login + Start Bitmessage ved brukerpålogging + + + + Start Bitmessage in the tray (don't show main window) + Start Bitmessage i systemstatusfeltet (ikke vis hovedvinduet) + + + + Minimize to tray + Minimiser til systemstatusfeltet + + + + Show notification when message received + Vis varsel når beskjed mottas + + + + Run in Portable Mode + Kjør i flyttbar modus + + + + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. + I flyttbar modus blir beskjeder og konfigurasjonsfiler oppbevart i samme katalog som programmet istedet for den vanlige applikasjonsdatamappen. Dette gjør Bitmessage enkel å kjøre fra f.eks. minnepinne. + + + + User Interface + Brukergrensesnitt + + + + Listening port + Lyttende port + + + + Listen for connections on port: + Lytt etter tilkoblinger på port: + + + + Proxy server / Tor + Proxytjener / Tor + + + + Type: + Type: + + + + none + ingen + + + + SOCKS4a + SOCKS4a + + + + SOCKS5 + SOCKS5 + + + + Server hostname: + Tjenernavn: + + + + Port: + Port: + + + + Authentication + Autentisering + + + + Username: + Brukernavn: + + + + Pass: + Passord: + + + + Network Settings + Nettverksinnstillinger + + + + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. + Når noen sender deg en beskjed må først datamaskin deres fullføre en arbeidsoppgave. Vanskelighetsgraden for denne oppgaven er satt til 1 som standard. Du kan heve denne grensen for nye adresser du oppretter ved å endre på verdiene her. Alle nye adresser du oppretter vil kreve av avsender å løse enda vanskeligere oppgaver. Det finnes èt unntak: Hvis du legger til en kontakt i din adressebok vil de bli varslet neste gang du sender en beskjed om at de kun trenger å utføre enkleste form for arbeidsoppgave, denne har vanskelighetsgrad 1. + + + + Total difficulty: + Total vanskelighet: + + + + Small message difficulty: + Vanskelighet for kort beskjed: + + + + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. + 'Vanskelighet for kort beskjed' vil kun påvirke sending av korte beskjeder. Dobling av denne verdien vil også doble vanskeligheten for å sende en kort beskjed. + + + + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. + 'Total vanskelighet' påvirker den absolutte mengden av arbeid som avsender må fullføre. Dobling av denne verdien dobler også arbeidsmengden. + + + + Demanded difficulty + Krevd vanskelighet + + + + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. + Her kan du sette den maksimale mengden med arbeid som du er villig til å gjennomføre for å sende en beskjed til en annen person. Om disse verdiene settes til null vil alle verdier bli akseptert. + + + + Maximum acceptable total difficulty: + Maks akseptabel total vanskelighet: + + + + Maximum acceptable small message difficulty: + Maks akseptabel vanskelighet for korte beskjeder: + + + + Max acceptable difficulty + Maks akseptabel vanskelighet + + + + Willingly include unencrypted destination address when sending to a mobile device + Inkluder med vilje ukrypterte destinasjonadresser når mobil enhet er mottaker + + + + Override automatic language localization (use countycode or language code, e.g. 'en_US' or 'en'): + Overstyr automatisk språklokalisering (bruk land- eller språkkode, f.eks. 'en_US' eller 'en'):) + + + + Listen for incoming connections when using proxy + Lytt etter innkommende tilkoblinger når proxy benyttes + + + + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> + <html><head/><body><p>Bitmessage kan benytte et annet Bitcoin-basert program ved navn Namecoin for å lage menneskevennlige adresser. For eksempel, istedet for å fortelle din kontakt din lange Bitmessage-adresse så kan du enkelt fortelle vedkommende at beskjeden skal sendes til <span style=" font-style:italic;">test. </span></p><p>(å få din egen adresse inn Namecoin er fortsatt ganske vanskelig).</p><p>Bitmessage kan bruke enten namecoind direkte eller en kjørende instans av nmcontrol.</p></body></html> + + + + Host: + Vert: + + + + Password: + Passord: + + + + Test + Test + + + + Connect to: + Koble til: + + + + Namecoind + Namecoind + + + + NMControl + NMControl + + + + Namecoin integration + Namecoin-integrasjon + + + + Use Identicons + Bruk identikoner + + + + Interface Language + Grensesnittspråk + + + + System Settings + system + Systeminnstillinger + + + + English + en + Engelsk + + + + Esperanto + eo + Esperanto + + + + Français + fr + Fransk + + + + Deutsch + de + Tysk + + + + Españl + es + Spansk + + + + русский язык + ru + Russisk + + + + Norwegian + no + Norsk + + + + Pirate English + en_pirate + Piratengelsk + + + + Other (set in keys.dat) + other + Annet (sett inn keys.dat) + + + + <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> + <html><head/><body><p>Som standard er det slik at hvis du sender en beskjed til noen og de er frakoblet i mer enn to dager så vil Bitmessage sende beskjeden på nytt igjen etter at det er gått to ekstra dager. Sånn vil det holde på fremover med eksponentiell vekst; beskjeder sendes på nytt etter 8, 16, 32 dager o.s.v., helt til mottakeren erkjenner dem. Her kan du endre på denne oppførselen ved å få Bitmessage til å gi opp etter et bestemt antall dager eller måneder.</p><p>La disse feltene stå tomme for å få standard oppsettet. </p></body></html> + + + + Give up after + Gi opp etter + + + + and + og + + + + days + dager + + + + months. + måneder. + + + + Resends Expire + Igjensending + + + diff --git a/src/translations/bitmessage_ru.pro b/src/translations/bitmessage_ru.pro index 0370961b..5d74581c 100644 --- a/src/translations/bitmessage_ru.pro +++ b/src/translations/bitmessage_ru.pro @@ -1,6 +1,7 @@ SOURCES = ../addresses.py\ ../bitmessagemain.py\ ../class_addressGenerator.py\ + ../class_objectProcessor.py\ ../class_outgoingSynSender.py\ ../class_receiveDataThread.py\ ../class_sendDataThread.py\ @@ -17,6 +18,7 @@ SOURCES = ../addresses.py\ ../shared.py\ ../bitmessageqt/__init__.py\ ../bitmessageqt/about.py\ + ../bitmessageqt/addaddressdialog.py\ ../bitmessageqt/bitmessageui.py\ ../bitmessageqt/connect.py\ ../bitmessageqt/help.py\ diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index a396165b..e67de685 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -1,200 +1,218 @@ + + AddAddressDialog + + + Add new entry + Добавить новую запись + + + + Label + Имя + + + + Address + Адрес + + MainWindow - + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? Один из Ваших адресов, %1, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас? - + Reply Ответить - + Add sender to your Address Book Добавить отправителя в адресную книгу - + Move to Trash Поместить в корзину - + View HTML code as formatted text Просмотреть HTML код как отформатированный текст - + Save message as... Сохранить сообщение как ... - + New Новый адрес - + Enable Включить - + Disable Выключить - + Copy address to clipboard Скопировать адрес в буфер обмена - + Special address behavior... Особое поведение адресов... - + Send message to this address Отправить сообщение на этот адрес - + Subscribe to this address Подписаться на рассылку с этого адреса - + Add New Address Добавить новый адрес - + Delete Удалить - + Copy destination address to clipboard Скопировать адрес отправки в буфер обмена - + Force send Форсировать отправку - + Add new entry Добавить новую запись - + Waiting for their encryption key. Will request it again soon. Ожидаем ключ шифрования от Вашего собеседника. Запрос будет повторен через некоторое время. - + Encryption key request queued. Запрос ключа шифрования поставлен в очередь. - + Queued. В очереди. - + Message sent. Waiting for acknowledgement. Sent at %1 Сообщение отправлено. Ожидаем подтверждения. Отправлено в %1 - + Need to do work to send message. Work is queued. Нужно провести требуемые вычисления, чтобы отправить сообщение. Вычисления ожидают очереди. - + Acknowledgement of the message received %1 Сообщение доставлено в %1 - + Broadcast queued. Рассылка ожидает очереди. - + Broadcast on %1 Рассылка на %1 - + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. %1 - + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. %1 - + Forced difficulty override. Send should start soon. Форсирована смена сложности. Отправляем через некоторое время. - + Unknown status: %1 %2 Неизвестный статус: %1 %2 - + Since startup on %1 С начала работы в %1 - + Not Connected Не соединено - + Show Bitmessage Показать Bitmessage - + Send Отправить - + Subscribe Подписки - + Address Book Адресная книга - + Quit Выйти - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. @@ -203,19 +221,19 @@ It is important that you back up this file. Создайте резервную копию этого файла перед тем как будете его редактировать. - + Open keys.dat? Открыть файл keys.dat? - + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) Вы можете управлять Вашими ключами, отредактировав файл keys.dat, находящийся в той же папке, что и эта программа. Создайте резервную копию этого файла перед тем как будете его редактировать. Хотели бы Вы открыть этот файл сейчас? (пожалуйста, закройте Bitmessage до того как Вы внесете в этот файл какие-либо изменения.) - + You may manage your keys by editing the keys.dat file stored in %1 It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) @@ -225,192 +243,192 @@ It is important that you back up this file. Would you like to open the file now? (пожалуйста, закройте Bitmessage до того как Вы внесете в этот файл какие-либо изменения.) - + Delete trash? Очистить корзину? - + Are you sure you want to delete all trashed messages? Вы уверены, что хотите очистить корзину? - + bad passphrase Неподходящая секретная фраза - + You must type your passphrase. If you don't have one then this is not the form for you. Вы должны ввести секретную фразу. Если Вы не хотите это делать, то Вы выбрали неправильную опцию. - + Processed %1 person-to-person messages. Обработано %1 сообщений. - + Processed %1 broadcast messages. Обработано %1 рассылок. - + Processed %1 public keys. Обработано %1 открытых ключей. - + Total Connections: %1 Всего соединений: %1 - + Connection lost Соединение потеряно - + Connected Соединено - + Message trashed Сообщение удалено - + Error: Bitmessage addresses start with BM- Please check %1 Ошибка: Bitmessage адреса начинаются с BM- Пожалуйста, проверьте %1 - + Error: The address %1 is not typed or copied correctly. Please check it. Ошибка: адрес %1 внесен или скопирован неправильно. Пожалуйста, перепроверьте. - + Error: The address %1 contains invalid characters. Please check it. Ошибка: адрес %1 содержит запрещенные символы. Пожалуйста, перепроверьте. - + Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. Ошибка: версия адреса в %1 слишком новая. Либо Вам нужно обновить Bitmessage, либо Ваш собеседник дал неправильный адрес. - + Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance. Ошибка: некоторые данные, закодированные в адресе %1, слишком короткие. Возможно, что-то не так с программой Вашего собеседника. - + Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance. Ошибка: некоторые данные, закодированные в адресе %1, слишком длинные. Возможно, что-то не так с программой Вашего собеседника. - + Error: Something is wrong with the address %1. Ошибка: что-то не так с адресом %1. - + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. Вы должны указать адрес в поле "От кого". Вы можете найти Ваш адрес во вкладе "Ваши Адреса". Sending to your address - Отправка на Ваш собственный адрес + Отправка на Ваш собственный адрес Error: One of the addresses to which you are sending a message, %1, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM. - Ошибка: Один из адресов, на который Вы отправляете сообщение, %1, принадлежит Вам. К сожалению, Bitmessage не может отправлять сообщения самому себе. Попробуйте запустить второго клиента на другом компьютере или на виртуальной машине. + Ошибка: Один из адресов, на который Вы отправляете сообщение, %1, принадлежит Вам. К сожалению, Bitmessage не может отправлять сообщения самому себе. Попробуйте запустить второго клиента на другом компьютере или на виртуальной машине. - + Address version number Версия адреса - + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем адреса версии %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Stream number Номер потока - + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. По поводу адреса %1: Bitmessage не поддерживаем стрим номер %2. Возможно, Вам нужно обновить клиент Bitmessage. - + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. Внимание: Вы не подключены к сети. Bitmessage проделает необходимые вычисления, чтобы отправить сообщение, но не отправит его до тех пор, пока Вы не подсоединитесь к сети. - + Your 'To' field is empty. Вы не заполнили поле 'Кому'. Work is queued. - Вычисления поставлены в очередь. + Вычисления поставлены в очередь. - + Right click one or more entries in your address book and select 'Send message to this address'. Нажмите правую кнопку мышки на каком-либо адресе и выберите "Отправить сообщение на этот адрес". - + Work is queued. %1 Вычисления поставлены в очередь. %1 - + New Message Новое сообщение - + From От - + Address is valid. Адрес введен правильно. - + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в Адресную Книгу несколько раз. Просто переименуйте существующий адрес. - + The address you entered was invalid. Ignoring it. Вы ввели неправильный адрес. Это адрес проигнорирован. - + Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в подписку несколько раз. Просто переименуйте существующую подписку. - + Restart Перезапустить - + You must restart Bitmessage for the port number change to take effect. Вы должны перезапустить Bitmessage, чтобы смена номера порта имела эффект. @@ -420,167 +438,167 @@ It is important that you back up this file. Would you like to open the file now? Bitmessage будет использовать Ваш прокси в дальнейшем, тем не менее, мы рекомендуем перезапустить Bitmessage в ручную, чтобы закрыть уже существующие соединения. - + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. Ошибка: Вы не можете добавлять один и тот же адрес в список несколько раз. Просто переименуйте существующий адрес. - + Passphrase mismatch Секретная фраза не подходит - + The passphrase you entered twice doesn't match. Try again. Вы ввели две разные секретные фразы. Пожалуйста, повторите заново. - + Choose a passphrase Придумайте секретную фразу - + You really do need a passphrase. Вы действительно должны ввести секретную фразу. - + All done. Closing user interface... Программа завершена. Закрываем пользовательский интерфейс... - + Address is gone Адрес утерян - + Bitmessage cannot find your address %1. Perhaps you removed it? Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его? - + Address disabled Адрес выключен - + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. Ошибка: адрес, с которого Вы пытаетесь отправить, выключен. Вам нужно будет включить этот адрес во вкладке "Ваши адреса". - + Entry added to the Address Book. Edit the label to your liking. Запись добавлена в Адресную Книгу. Вы можете ее отредактировать. - + Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. Удалено в корзину. Чтобы попасть в корзину, Вам нужно будет найти файл корзины на диске. - + Save As... Сохранить как ... - + Write error. Ошибка записи. - + No addresses selected. Вы не выбрали адрес. Options have been disabled because they either aren't applicable or because they haven't yet been implemented for your operating system. - Опции были отключены, потому что ли они либо не подходят, либо еще не выполнены под Вашу операционную систему. + Опции были отключены, потому что ли они либо не подходят, либо еще не выполнены под Вашу операционную систему. - + The address should start with ''BM-'' Адрес должен начинаться с "BM-" - + The address is not typed or copied correctly (the checksum failed). Адрес введен или скопирован неверно (контрольная сумма не сходится). - + The version number of this address is higher than this software can support. Please upgrade Bitmessage. Версия этого адреса более поздняя, чем Ваша программа. Пожалуйста, обновите программу Bitmessage. - + The address contains invalid characters. Адрес содержит запрещенные символы. - + Some data encoded in the address is too short. Данные, закодированные в адресе, слишком короткие. - + Some data encoded in the address is too long. Данные, закодированные в адресе, слишком длинные. - + You are using TCP port %1. (This can be changed in the settings). Вы используете TCP порт %1 (Его можно поменять в настройках). - + Bitmessage Bitmessage - + To Кому - + From От кого - + Subject Тема - + Received Получено - + Inbox Входящие - + Load from Address book Взять из адресной книги - + Message: Сообщение: - + Subject: Тема: - + Send to one or more specific people Отправить одному или нескольким указанным получателям @@ -598,312 +616,312 @@ p, li { white-space: pre-wrap; } <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - + To: Кому: - + From: От: - + Broadcast to everyone who is subscribed to your address Рассылка всем, кто подписался на Ваш адрес - + Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. Пожалуйста, учитывайте, что рассылки шифруются лишь Вашим адресом. Любой человек, который знает Ваш адрес, сможет прочитать Вашу рассылку. - + Status Статус - + Sent Отправленные - + Label (not shown to anyone) Имя (не показывается никому) - + Address Адрес - + Stream Поток - + Your Identities Ваши Адреса - + Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. Здесь Вы можете подписаться на рассылки от других пользователей. Все рассылки будут появляться у Вас во Входящих. Вы будете следить за всеми адресами, указанными здесь, даже если они в черном списке. - + Add new Subscription Добавить новую подписку - + Label Имя - + Subscriptions Подписки - + The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. Адресная книга удобна для присвоения осмысленных имен Bitmessage адресам Ваших друзей. Вы можете добавлять новые записи с помощью кнопки "Добавить новую запись", или же правым кликом мышки на сообщении. - + Name or Label Имя - + Use a Blacklist (Allow all incoming messages except those on the Blacklist) Использовать черный список (Разрешить все входящие сообщения, кроме указанных в черном списке) - + Use a Whitelist (Block all incoming messages except those on the Whitelist) Использовать белый список (блокировать все входящие сообщения, кроме указанных в белом списке) - + Blacklist Черный список - + Stream # № потока - + Connections Соединений - + Total connections: 0 Всего соединений: 0 - + Since startup at asdf: С начала работы программы в asdf: - + Processed 0 person-to-person message. Обработано 0 сообщений. - + Processed 0 public key. Обработано 0 открытых ключей. - + Processed 0 broadcast. Обработано 0 рассылок. - + Network Status Статус сети - + File Файл - + Settings Настройки - + Help Помощь - + Import keys Импортировать ключи - + Manage keys Управлять ключами - + About О программе - + Regenerate deterministic addresses Сгенерировать заново все адреса - + Delete all trashed messages Стереть все сообщения из корзины - + Message sent. Sent at %1 Сообщение отправлено в %1 - + Chan name needed Требуется имя chan-а - + You didn't enter a chan name. Вы не ввели имя chan-a. - + Address already present Адрес уже существует - + Could not add chan because it appears to already be one of your identities. Не могу добавить chan, потому что это один из Ваших уже существующих адресов. - + Success Отлично - + Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'. Chan был успешно создан. Чтобы добавить других в сhan, сообщите им имя chan-а и этот Bitmessage адрес: %1. Этот адрес также отображается во вкладке "Ваши Адреса". - + Address too new Адрес слишком новый - + Although that Bitmessage address might be valid, its version number is too new for us to handle. Perhaps you need to upgrade Bitmessage. Этот Bitmessage адрес похож на правильный, но его версия этого адреса слишком новая. Возможно, Вам необходимо обновить программу Bitmessage. - + Address invalid Неправильный адрес - + That Bitmessage address is not valid. Этот Bitmessage адрес введен неправильно. - + Address does not match chan name Адрес не сходится с именем chan-а - + Although the Bitmessage address you entered was valid, it doesn't match the chan name. Вы ввели верный адрес Bitmessage, но он не сходится с именем chan-а. - + Successfully joined chan. Успешно присоединились к chan-у. - + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). Bitmessage будет использовать Ваш прокси, начиная прямо сейчас. Тем не менее Вам имеет смысл перезапустить Bitmessage, чтобы закрыть уже существующие соединения. - + This is a chan address. You cannot use it as a pseudo-mailing list. Это адрес chan-а. Вы не можете его использовать как адрес рассылки. - + Search Поиск - + All По всем полям - + Message Текст сообщения - + Join / Create chan Подсоединиться или создать chan - + Mark Unread Отметить как непрочитанное - + Fetched address from namecoin identity. Получить адрес через Namecoin. - + Testing... Проверяем... - + Fetch Namecoin ID Получить Namecoin ID - + Ctrl+Q Ctrl+Q - + F1 F1 - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -915,6 +933,96 @@ p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + + + Set avatar... + + + + + Bad address version number + + + + + Your address version number must be a number: either 3 or 4. + + + + + Your address version number must be either 3 or 4. + + + + + Inventory lookups per second: %1 + + + + + Will not resend ever + + + + + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. + + + + + Do you really want to remove this avatar? + + + + + You have already set an avatar for this address. Do you really want to overwrite it? + + + + + Start-on-login not yet supported on your OS. + + + + + Minimize-to-tray not yet supported on your OS. + + + + + Tray notifications not yet supported on your OS. + + + + + Enter an address above. + + + + + Address is an old type. We cannot display its past broadcasts. + + + + + There are no recent broadcasts from this address to display. + + + + + Display the %1 recent broadcast from this address. + + + + + Display the %1 recent broadcasts from this address. + + + + + Inventory lookups per second: 0 + + NewAddressDialog @@ -957,7 +1065,7 @@ The 'Random Number' option is selected by default but deterministic ad Address version number: 3 - Версия адреса: 3 + Версия адреса: 3 @@ -1014,6 +1122,11 @@ The 'Random Number' option is selected by default but deterministic ad (saves you some bandwidth and processing power) (немного сэкономит Вам пропускную способность сети и вычислительную мощь) + + + Address version number: 4 + Версия адреса: 4 + NewChanDialog @@ -1061,20 +1174,25 @@ The 'Random Number' option is selected by default but deterministic ad NewSubscriptionDialog - + Add new entry Добавить новую запись - + Label Имя - + Address Адрес + + + CheckBox + + SpecialAddressBehaviorDialog @@ -1107,35 +1225,40 @@ The 'Random Number' option is selected by default but deterministic ad aboutDialog - + About О программе - + PyBitmessage PyBitmessage - + version ? версия ? Copyright © 2013 Jonathan Warren - Копирайт © 2013 Джонатан Уоррен + Копирайт © 2013 Джонатан Уоррен - + <html><head/><body><p>Distributed under the MIT/X11 software license; see <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> <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> - + This is Beta software. Это бета версия программы. + + + <html><head/><body><p>Copyright © 2012-2013 Jonathan Warren<br/>Copyright © 2013 The Bitmessage Developers</p></body></html> + + connectDialog @@ -1257,299 +1380,304 @@ The 'Random Number' option is selected by default but deterministic ad regenerateAddressesDialog - + Regenerate Existing Addresses Сгенерировать заново существующие адреса - + Regenerate existing addresses Сгенерировать заново существующие адреса - + Passphrase Секретная фраза - + Number of addresses to make based on your passphrase: Кол-во адресов, которые Вы хотите получить из Вашей секретной фразы: Address version Number: - Версия адреса: + Версия адреса: 3 - 3 + 3 - + Stream number: Номер потока: - + 1 1 - + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter Потратить несколько лишних минут, чтобы сделать адрес(а) короче на 1 или 2 символа - + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. Вы должны кликнуть эту галочку (или не кликать) точно также как Вы сделали в самый первый раз, когда создавали Ваши адреса. - + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. Если Вы ранее делали детерминистические адреса, но случайно потеряли их, Вы можете их восстановить здесь. Если же Вы использовали генератор случайных чисел, чтобы создать Ваши адреса, то Вы не сможете их здесь восстановить. + + + Address version number: + + settingsDialog - + Settings Настройки - + Start Bitmessage on user login Запускать Bitmessage при входе в систему - + Start Bitmessage in the tray (don't show main window) Запускать Bitmessage в свернутом виде (не показывать главное окно) - + Minimize to tray Сворачивать в трей - + Show notification when message received Показывать уведомления при получении новых сообщений - + Run in Portable Mode Запустить в переносном режиме - + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. В переносном режиме, все сообщения и конфигурационные файлы сохраняются в той же самой папке что и сама программа. Это делает более удобным использование Bitmessage с USB-флэшки. - + User Interface Пользовательские - + Listening port Порт прослушивания - + Listen for connections on port: Прослушивать соединения на порту: - + Proxy server / Tor Прокси сервер / Tor - + Type: Тип: - + none отсутствует - + SOCKS4a SOCKS4a - + SOCKS5 SOCKS5 - + Server hostname: Адрес сервера: - + Port: Порт: - + Authentication Авторизация - + Username: Имя пользователя: - + Pass: Прль: - + Network Settings Сетевые настройки - + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. Когда кто-либо отправляет Вам сообщение, его компьютер должен сперва решить определенную вычислительную задачу. Сложность этой задачи по умолчанию равна 1. Вы можете повысить эту сложность для новых адресов, которые Вы создадите, здесь. Таким образом, любые новые адреса, которые Вы создадите, могут требовать от отправителей сложность большую чем 1. Однако, есть одно исключение: если Вы специально добавите Вашего собеседника в адресную книгу, то Bitmessage автоматически уведомит его о том, что для него минимальная сложность будет составлять всегда всего лишь 1. - + Total difficulty: Общая сложность: - + Small message difficulty: Сложность для маленьких сообщений: - + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. "Сложность для маленьких сообщений" влияет исключительно на небольшие сообщения. Увеличив это число в два раза, вы сделаете отправку маленьких сообщений в два раза сложнее, в то время как сложность отправки больших сообщений не изменится. - + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. "Общая сложность" влияет на абсолютное количество вычислений, которые отправитель должен провести, чтобы отправить сообщение. Увеличив это число в два раза, вы увеличите в два раза объем требуемых вычислений. - + Demanded difficulty Требуемая сложность - + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. Здесь Вы можете установить максимальную вычислительную работу, которую Вы согласны проделать, чтобы отправить сообщение другому пользователю. Ноль означает, чтобы любое значение допустимо. - + Maximum acceptable total difficulty: Макс допустимая общая сложность: - + Maximum acceptable small message difficulty: Макс допустимая сложность для маленький сообщений: - + Max acceptable difficulty Макс допустимая сложность - + Listen for incoming connections when using proxy Прослушивать входящие соединения если используется прокси - + Willingly include unencrypted destination address when sending to a mobile device Специально прикреплять незашифрованный адрес получателя, когда посылаем на мобильное устройство - + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> <html><head/><body><p>Bitmessage умеет пользоваться программой Namecoin для того, чтобы сделать адреса более дружественными для пользователей. Например, вместо того, чтобы диктовать Вашему другу длинный и нудный адрес Bitmessage, Вы можете попросить его отправить сообщение на адрес вида <span style=" font-style:italic;">test. </span></p><p>(Перенести Ваш Bitmessage адрес в Namecoin по-прежнему пока довольно сложно).</p><p>Bitmessage может использовать либо прямо namecoind, либо уже запущенную программу nmcontrol.</p></body></html> - + Host: Адрес: - + Password: Пароль: - + Test Проверить - + Connect to: Подсоединиться к: - + Namecoind Namecoind - + NMControl NMControl - + Namecoin integration Интеграция с Namecoin - + Interface Language Язык интерфейса - + System Settings system Язык по умолчанию - + English en English - + Esperanto eo Esperanto - + Français fr Francais - + Deutsch de Deutsch @@ -1558,25 +1686,78 @@ The 'Random Number' option is selected by default but deterministic ad Español es - Espanol + Espanol Русский ru - Русский + Русский - + Pirate English en_pirate Pirate English - + Other (set in keys.dat) other Другие (настроено в keys.dat) + + + Use Identicons + + + + + Españl + es + + + + + русский язык + ru + + + + + Norsk + no + + + + + <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> + + + + + Give up after + + + + + and + + + + + days + + + + + months. + + + + + Resends Expire + + diff --git a/src/translations/bitmessage_zh_cn.pro b/src/translations/bitmessage_zh_cn.pro new file mode 100644 index 00000000..cde33c9f --- /dev/null +++ b/src/translations/bitmessage_zh_cn.pro @@ -0,0 +1,35 @@ +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\ + ../helper_bootstrap.py\ + ../helper_generic.py\ + ../helper_inbox.py\ + ../helper_sent.py\ + ../helper_startup.py\ + ../shared.py\ + ../bitmessageqt/__init__.py\ + ../bitmessageqt/about.py\ + ../bitmessageqt/addaddressdialog.py\ + ../bitmessageqt/bitmessageui.py\ + ../bitmessageqt/connect.py\ + ../bitmessageqt/help.py\ + ../bitmessageqt/iconglossary.py\ + ../bitmessageqt/newaddressdialog.py\ + ../bitmessageqt/newchandialog.py\ + ../bitmessageqt/newsubscriptiondialog.py\ + ../bitmessageqt/regenerateaddresses.py\ + ../bitmessageqt/settings.py\ + ../bitmessageqt/specialaddressbehavior.py + + +TRANSLATIONS = bitmessage_zh_cn.ts +CODECFORTR = UTF-8 diff --git a/src/translations/bitmessage_zh_cn.qm b/src/translations/bitmessage_zh_cn.qm new file mode 100644 index 00000000..dce69347 Binary files /dev/null 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 new file mode 100644 index 00000000..1812e4bf --- /dev/null +++ b/src/translations/bitmessage_zh_cn.ts @@ -0,0 +1,1645 @@ + + + + + AddAddressDialog + + + Add new entry + 添加新条目 + + + + Label + 标签 + + + + Address + 地址 + + + + MainWindow + + + Bitmessage + 比特信 + + + + Search + 搜索 + + + + All + 全部 + + + + To + + + + + From + 来自 + + + + Subject + 标题 + + + + Message + 消息 + + + + Received + 接收时间 + + + + Inbox + 收件箱 + + + + Load from Address book + 从地址本中选择 + + + + Fetch Namecoin ID + 接收Namecoin ID + + + + Message: + 消息: + + + + Subject: + 标题: + + + + Send to one or more specific people + 发送给一个或更多指定的人 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> + + + + + To: + 至: + + + + From: + 来自: + + + + Broadcast to everyone who is subscribed to your address + 广播给全部订阅到您的地址的人 + + + + Send + 发送 + + + + Be aware that broadcasts are only encrypted with your address. Anyone who knows your address can read them. + 请注意,广播的消息仅仅使用您的地址加密。任何知道您的地址的人可以阅读其中的内容。 + + + + Status + 状态 + + + + Sent + 已发送 + + + + New + 新建 + + + + Label (not shown to anyone) + 标签(只有您看的到) + + + + Address + 地址 + + + + Stream + 节点流 + + + + Your Identities + 您的身份 + + + + Here you can subscribe to 'broadcast messages' that are sent by other users. Messages will appear in your Inbox. Addresses here override those on the Blacklist tab. + 您可以在这里订阅到广播地址,接收来自其他用户的广播。消息将出现在您的收件箱。您的黑名单对在这里的地址无效。 + + + + Add new Subscription + 添加新的订阅 + + + + Label + 标签 + + + + Subscriptions + 订阅 + + + + The Address book is useful for adding names or labels to other people's Bitmessage addresses so that you can recognize them more easily in your inbox. You can add entries here using the 'Add' button, or from your inbox by right-clicking on a message. + 这个地址本将帮助您给其他人的地址添加名字或标签,这样的话您就可以在收件箱中更容易的认出它们。您可以在这里点“新建”,或在一条在收件箱中的消息上右击来添加条目。 + + + + Add new entry + 添加新条目 + + + + Name or Label + 名称或标签 + + + + Address Book + 地址本 + + + + Use a Blacklist (Allow all incoming messages except those on the Blacklist) + 使用黑名单(允许所有黑名单以外的人向您发送消息) + + + + Use a Whitelist (Block all incoming messages except those on the Whitelist) + 使用白名单(仅允许在白名单上的人向您发送消息) + + + + Blacklist + 黑名单 + + + + Stream # + 节点流 # + + + + Connections + 连接 + + + + Total connections: 0 + 总连接数: 0 + + + + Since startup at asdf: + 自启动于 asdf: + + + + Processed 0 person-to-person message. + 处理了 0 个点对点消息。 + + + + Processed 0 public key. + 处理了 0 个公匙。 + + + + Processed 0 broadcast. + 处理了 0 个广播。 + + + + Inventory lookups per second: 0 + 每秒种的同步请求数: 0 + + + + Network Status + 网络状态 + + + + File + 文件 + + + + Settings + 设置 + + + + Help + 帮助 + + + + Import keys + 导入密钥 + + + + Manage keys + 管理密钥 + + + + Quit + 退出 + + + + Ctrl+Q + Ctrl+Q + + + + F1 + F1 + + + + About + 关于 + + + + Regenerate deterministic addresses + 重新生成静态地址 + + + + Delete all trashed messages + 彻底删除全部回收站中的消息 + + + + Join / Create chan + 加入或创建一个频道 + + + + Reply + 回复 + + + + Add sender to your Address Book + 将发送者添加到地址本 + + + + Move to Trash + 移入回收站 + + + + View HTML code as formatted text + 作为HTML查看 + + + + Save message as... + 将消息保存为... + + + + Mark Unread + 标记为未读 + + + + Enable + 启用 + + + + Disable + 禁用 + + + + Set avatar... + 设置头像... + + + + Copy address to clipboard + 将地址复制到剪贴板 + + + + Special address behavior... + 特别的地址行为... + + + + Send message to this address + 发送消息到这个地址 + + + + Subscribe to this address + 订阅到这个地址 + + + + Add New Address + 创建新地址 + + + + Delete + 删除 + + + + Copy destination address to clipboard + 复制目标地址到剪贴板 + + + + Force send + 强制发送 + + + + One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now? + 您的地址中的一个, %1,是一个过时的版本1地址. 版本1地址已经不再受到支持了. 我们可以将它删除掉么? + + + + Since startup on %1 + 自启动于 %1 + + + + Waiting on their encryption key. Will request it again soon. + 正在等待他们的加密密钥,我们会在稍后再次请求。 + + + + Encryption key request queued. + 加密密钥请求已经添加到队列中。 + + + + Queued. + 已经添加到队列。 + + + + Message sent. Waiting on acknowledgement. Sent at %1 + 消息已经发送. 正在等待回执. 发送于 %1 + + + + Message sent. Sent at %1 + 消息已经发送. 发送于 %1 + + + + Need to do work to send message. Work is queued. + 发生消息需要做工。做工正在队列中等待。 + + + + Acknowledgement of the message received %1 + 消息的回执已经收到于 %1 + + + + Broadcast queued. + 广播已经添加到队列中。 + + + + Broadcast on %1 + 已经广播于 %1 + + + + Problem: The work demanded by the recipient is more difficult than you are willing to do. %1 + 错误: 收件人要求的做工量大于我们的最大接受做工量。 %1 + + + + Problem: The recipient's encryption key is no good. Could not encrypt message. %1 + 错误: 收件人的加密密钥是无效的。不能加密消息。 %1 + + + + Forced difficulty override. Send should start soon. + 已经忽略最大做工量限制。发送很快就会开始。 + + + + Unknown status: %1 %2 + 未知状态: %1 %2 + + + + Not Connected + 未连接 + + + + Show Bitmessage + 显示比特信 + + + + Subscribe + 订阅 + + + + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. + 您可以通过编辑和程序储存在同一个目录的 keys.dat 来编辑密钥。备份这个文件十分重要。 + + + + You may manage your keys by editing the keys.dat file stored in + %1 +It is important that you back up this file. + 您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。 + + + + Open keys.dat? + 打开 keys.dat ? + + + + You may manage your keys by editing the keys.dat file stored in the same directory as this program. It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) + 您可以通过编辑和程序储存在同一个目录的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信) + + + + You may manage your keys by editing the keys.dat file stored in + %1 +It is important that you back up this file. Would you like to open the file now? (Be sure to close Bitmessage before making any changes.) + 您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信) + + + + Delete trash? + 清空回收站? + + + + Are you sure you want to delete all trashed messages? + 您确定要删除全部被回收的消息么? + + + + bad passphrase + 错误的密钥 + + + + You must type your passphrase. If you don't have one then this is not the form for you. + 您必须输入您的密钥。如果您没有的话,这个表单不适用于您。 + + + + Bad address version number + 地址的版本号无效 + + + + Your address version number must be a number: either 3 or 4. + 您的地址的版本号必须是一个数字: 3 或 4. + + + + Your address version number must be either 3 or 4. + 您的地址的版本号必须是 3 或 4. + + + + Chan name needed + 需要频道的名称 + + + + You didn't enter a chan name. + 您没有输入一个频道的名称。 + + + + Address already present + 地址已经在这里了 + + + + Could not add chan because it appears to already be one of your identities. + 无法添加频道,因为它似乎已经是您的身份之一。 + + + + Success + 成功 + + + + Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'. + 成功的创建了频道。要让他人加入,请告诉他们频道的名称和这个比特信地址 %1 。这个比特信地址也会出现在“您的身份”中。 + + + + Address too new + 地址太新了 + + + + Although that Bitmessage address might be valid, its version number is too new for us to handle. Perhaps you need to upgrade Bitmessage. + 尽管比特信地址也许是有效的,不过比特信地址的版本号比我们能处理的要新。也许您应该升级比特信了。 + + + + Address invalid + 地址有效 + + + + That Bitmessage address is not valid. + 比特信地址无效。 + + + + Address does not match chan name + 地址和频道的名称不符 + + + + Although the Bitmessage address you entered was valid, it doesn't match the chan name. + 尽管您输入的比特信地址是有效的,不过它和频道的名称不符。 + + + + Successfully joined chan. + 成功的加入到频道。 + + + + Processed %1 person-to-person messages. + 处理了 %1 个点对点消息。 + + + + Processed %1 broadcast messages. + 处理了 %1 个广播。 + + + + Processed %1 public keys. + 处理了 %1 个公匙。 + + + + Total Connections: %1 + 总连接数: %1 + + + + Inventory lookups per second: %1 + 每秒种的同步请求数: %1 + + + + Connection lost + 连接已丢失 + + + + Connected + 已经连接 + + + + Message trashed + 消息已经移入回收站 + + + + Error: Bitmessage addresses start with BM- Please check %1 + 错误:比特信地址以BM-开始,请检查 %1 + + + + Error: The address %1 is not typed or copied correctly. Please check it. + 错误:地址 %1 没有被正确的键入或复制。 请检查一下。 + + + + Error: The address %1 contains invalid characters. Please check it. + 错误: 比特信地址 %1 包含无效的字符。请检查一下。 + + + + Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever. + 错误:地址 %1 的版本号过高。您可能需要升级您的比特信软件或者您的朋友正在使用本程序的非主线版本。 + + + + Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance. + 错误:在地址 %1 中编码的部分信息过短。您的朋友的软件可能有点问题。 + + + + Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance. + 错误:在地址 %1 中编码的部分信息过长。您的朋友的软件可能有点问题。 + + + + Error: Something is wrong with the address %1. + 错误: 地址%1 有为未知的错误。 + + + + Error: You must specify a From address. If you don't have one, go to the 'Your Identities' tab. + 错误: 您必须指出一个表单地址, 如果您没有,请到“您的身份”标签页。 + + + + Address version number + 地址版本号 + + + + Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version. + 地址 %1 的地址版本号 %2 无法被比特信理解。也许你应该升级你的比特信到最新版本。 + + + + Stream number + 节点流序号 + + + + Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version. + 地址 %1 的节点流序号 %2 无法被比特信理解。也许你应该升级你的比特信到最新版本。 + + + + Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won't send until you connect. + 警告: 您尚未连接。 比特信将做足够的功来发送消息,但是消息不会被发出直到您连接。 + + + + Your 'To' field is empty. + “收件人"是空的。 + + + + Right click one or more entries in your address book and select 'Send message to this address'. + 在您的地址本的一个条目上右击,之后选择”发送消息到这个地址“。 + + + + Fetched address from namecoin identity. + 已经自namecoin接收了地址。 + + + + Work is queued. %1 + 做工已经添加到队列中。 %1 + + + + New Message + 新消息 + + + + From + 来自 + + + + Address is valid. + 地址有效。 + + + + The address you entered was invalid. Ignoring it. + 您输入的地址是无效的,将被忽略。 + + + + Error: You cannot add the same address to your address book twice. Try renaming the existing one if you want. + 错误:您无法将一个地址添加到您的地址本两次,请尝试重命名已经存在的那个。 + + + + Error: You cannot add the same address to your subsciptions twice. Perhaps rename the existing one if you want. + 错误:您无法将一个地址添加到您的订阅两次,也许您想重命名已经存在的那个。 + + + + Restart + 重启 + + + + You must restart Bitmessage for the port number change to take effect. + 您必须重启以便使比特信对于使用的端口的改变生效。 + + + + Bitmessage will use your proxy from now on but you may want to manually restart Bitmessage now to close existing connections (if any). + 比特信将会从现在开始使用代理,但是您可能想手动重启比特信以便使之前的连接关闭(如果有的话)。 + + + + Will not resend ever + 不尝试再次发送 + + + + Note that the time limit you entered is less than the amount of time Bitmessage waits for the first resend attempt therefore your messages will never be resent. + 请注意,您所输入的时间限制小于比特信的最小重试时间,因此您将永远不会重发消息。 + + + + Error: You cannot add the same address to your list twice. Perhaps rename the existing one if you want. + 错误:您无法将一个地址添加到您的列表两次,也许您想重命名已经存在的那个。 + + + + Passphrase mismatch + 密钥不匹配 + + + + The passphrase you entered twice doesn't match. Try again. + 您两次输入的密码并不匹配,请再试一次。 + + + + Choose a passphrase + 选择一个密钥 + + + + You really do need a passphrase. + 您真的需要一个密码。 + + + + All done. Closing user interface... + 全部完成,正在关闭用户界面... + + + + Address is gone + 已经失去了地址 + + + + Bitmessage cannot find your address %1. Perhaps you removed it? + 比特信无法找到你的地址 %1。 也许你已经把它删掉了? + + + + Address disabled + 地址已经禁用 + + + + Error: The address from which you are trying to send is disabled. You'll have to enable it on the 'Your Identities' tab before using it. + 错误: 您想以一个您已经禁用的地址发出消息。在使用之前您需要在“您的身份”处再次启用。 + + + + Entry added to the Address Book. Edit the label to your liking. + 条目已经添加到地址本。您可以去修改您的标签。 + + + + Moved items to trash. There is no user interface to view your trash, but it is still on disk if you are desperate to get it back. + 已经移动项目到回收站。没有图形化的界面可以查看您的回收站,不过如果您还想找回的化它还在您的硬盘上。 + + + + Save As... + 另存为... + + + + Write error. + 写入失败。 + + + + No addresses selected. + 没有选择地址。 + + + + Do you really want to remove this avatar? + 您真的想一处这个头像么? + + + + You have already set an avatar for this address. Do you really want to overwrite it? + 您已经为这个地址设置了头像了。您真的想移除么? + + + + Start-on-login not yet supported on your OS. + 登录时启动尚未支持您在使用的操作系统。 + + + + Minimize-to-tray not yet supported on your OS. + 最小化到托盘尚未支持您的操作系统。 + + + + Tray notifications not yet supported on your OS. + 托盘提醒尚未支持您所使用的操作系统。 + + + + Testing... + 正在测试... + + + + This is a chan address. You cannot use it as a pseudo-mailing list. + 这是一个频道地址,您无法把它作为伪邮件列表。 + + + + The address should start with ''BM-'' + 地址应该以"BM-"开始 + + + + The address is not typed or copied correctly (the checksum failed). + 地址没有被正确的键入或复制(校验码校验失败)。 + + + + The version number of this address is higher than this software can support. Please upgrade Bitmessage. + 这个地址的版本号大于此软件的最大支持。 请升级比特信。 + + + + The address contains invalid characters. + 这个地址中包含无效字符。 + + + + Some data encoded in the address is too short. + 在这个地址中编码的部分信息过少。 + + + + Some data encoded in the address is too long. + 在这个地址中编码的部分信息过长。 + + + + Enter an address above. + 请在上方键入地址。 + + + + Address is an old type. We cannot display its past broadcasts. + 地址没有近期的广播。我们无法显示之间的广播。 + + + + There are no recent broadcasts from this address to display. + 没有可以显示的近期广播。 + + + + Display the %1 recent broadcast from this address. + 显示 %1 条近期广播。 + + + + Display the %1 recent broadcasts from this address. + 显示 %1 条近期广播。 + + + + You are using TCP port %1. (This can be changed in the settings). + 您正在使用TCP端口 %1 。(可以在设置中修改)。 + + + + Waiting for their encryption key. Will request it again soon. + 正在等待他们的加密密钥,我们会在稍后再次请求。 + + + + Message sent. Waiting for acknowledgement. Sent at %1 + 消息已经发送. 正在等待回执. 发送于 %1 + + + + NewAddressDialog + + + Create new Address + 创建新地址 + + + + Here you may generate as many addresses as you like. Indeed, creating and abandoning addresses is encouraged. You may generate addresses by using either random numbers or by using a passphrase. If you use a passphrase, the address is called a "deterministic" address. +The 'Random Number' option is selected by default but deterministic addresses have several pros and cons: + 在这里,您想创建多少地址就创建多少。诚然,创建和丢弃地址受到鼓励。你既可以使用随机数来创建地址,也可以使用密钥。如果您使用密钥的话,生成的地址叫“静态地址”。随机数选项默认为选择,不过相比而言静态地址既有缺点也有优点: + + + + <html><head/><body><p><span style=" font-weight:600;">Pros:<br/></span>You can recreate your addresses on any computer from memory. <br/>You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. <br/><span style=" font-weight:600;">Cons:<br/></span>You must remember (or write down) your passphrase if you expect to be able to recreate your keys if they are lost. <br/>You must remember the address version number and the stream number along with your passphrase. <br/>If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you.</p></body></html> + <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> + + + + Use a random number generator to make an address + 使用随机数生成地址 + + + + Use a passphrase to make addresses + 使用密钥生成地址 + + + + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter + 花费数分钟的计算使地址短1-2个字母 + + + + Make deterministic addresses + 创建静态地址 + + + + Address version number: 4 + 地址版本号:4 + + + + In addition to your passphrase, you must remember these numbers: + 在记住您的密钥的同时,您还需要记住以下数字: + + + + Passphrase + 密钥 + + + + Number of addresses to make based on your passphrase: + 使用该密钥生成的地址数: + + + + Stream number: 1 + 节点流序号:1 + + + + Retype passphrase + 再次输入密钥 + + + + Randomly generate address + 随机生成地址 + + + + Label (not shown to anyone except you) + 标签(只有您看的到) + + + + Use the most available stream + 使用最可用的节点流 + + + + (best if this is the first of many addresses you will create) + 如果这是您创建的数个地址中的第一个时最佳 + + + + Use the same stream as an existing address + 使用和如下地址一样的节点流 + + + + (saves you some bandwidth and processing power) + (节省你的带宽和处理能力) + + + + NewSubscriptionDialog + + + Add new entry + 添加新条目 + + + + Label + 标签 + + + + Address + 地址 + + + + CheckBox + 显示在添加之前2天内的广播 + + + + SpecialAddressBehaviorDialog + + + Special Address Behavior + 特别的地址行为 + + + + Behave as a normal address + 作为普通地址 + + + + Behave as a pseudo-mailing-list address + 作为伪邮件列表地址 + + + + Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public). + 伪邮件列表收到消息时会自动将其公开的广播给订阅者。 + + + + Name of the pseudo-mailing-list: + 伪邮件列表名称: + + + + aboutDialog + + + About + 关于 + + + + PyBitmessage + PyBitmessage + + + + version ? + 版本 ? + + + + <html><head/><body><p>Copyright © 2012-2013 Jonathan Warren<br/>Copyright © 2013 The Bitmessage Developers</p></body></html> + <html><head/><body><p>版权所有 © 2012-2013 Jonathan Warren<br/>版权所有 © 2013 比特信开发者</p></body></html> + + + + <html><head/><body><p>Distributed under the MIT/X11 software license; see <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> + <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> + + + + This is Beta software. + 本软件处于Beta阶段。 + + + + connectDialog + + + Bitmessage + 比特信 + + + + Bitmessage won't connect to anyone until you let it. + 除非您允许,比特信不会连接到任何人。 + + + + Connect now + 现在连接 + + + + Let me configure special network settings first + 请先让我进行特别的网络设置 + + + + helpDialog + + + Help + 帮助 + + + + <a href="http://Bitmessage.org/wiki/PyBitmessage_Help">http://Bitmessage.org/wiki/PyBitmessage_Help</a> + <a href="http://Bitmessage.org/wiki/PyBitmessage_Help">http://Bitmessage.org/wiki/PyBitmessage_Help</a> + + + + As Bitmessage is a collaborative project, help can be found online in the Bitmessage Wiki: + 鉴于比特信是一个共同完成的项目,您可以在比特信的Wiki上了解如何帮助比特信: + + + + iconGlossaryDialog + + + Icon Glossary + 图标含义 + + + + You have no connections with other peers. + 您没有和其他节点的连接. + + + + You have made at least one connection to a peer using an outgoing connection but you have not yet received any incoming connections. Your firewall or home router probably isn't configured to forward incoming TCP connections to your computer. Bitmessage will work just fine but it would help the Bitmessage network if you allowed for incoming connections and will help you be a better-connected node. + 你有至少一个到其他节点的出站连接,但是尚未收到入站连接。您的防火墙或路由器可能尚未设置转发入站TCP连接到您的电脑。比特信将正常运行,不过如果您允许入站连接的话将帮助比特信网络并成为一个通信状态更好的节点。 + + + + You are using TCP port ?. (This can be changed in the settings). + 您正在使用TCP端口 ? 。(可以在设置中更改). + + + + You do have connections with other peers and your firewall is correctly configured. + 您有和其他节点的连接且您的防火墙已经正确配置。 + + + + newChanDialog + + + Dialog + 对话框 + + + + Create a new chan + 创建一个新的频道 + + + + Join a chan + 加入一个频道 + + + + Create a chan + 创建一个频道 + + + + <html><head/><body><p>Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. If you and someone else both create a chan with the same chan name then it is currently very likely that they will be the same chan.</p></body></html> + <html><head/><body><p>为您的频道起一个名字。如果您选择了一个足够难的名字(比如一个唯一而且强度很高的密码)而您的朋友们也没有公开这个名字,那么频道将会是私密并安全的。目前看来,如果有人和您使用相同的名字创建频道,创建的频道将和您的相同。</p></body></html> + + + + Chan name: + 频道名称: + + + + <html><head/><body><p>A chan exists when a group of people share the same decryption keys. The keys and bitmessage address used by a chan are generated from a human-friendly word or phrase (the chan name). To send a message to everyone in the chan, send a normal person-to-person message to the chan address.</p><p>Chans are experimental and completely unmoderatable.</p></body></html> + <html><head/><body><p>一个频道存在于一群共有同一个解密密钥的人之间。频道的密钥和比特信地址生成自可读的文字或密码(频道的名字)。要给一个频道中的每一个人发送消息,仅仅需要发送一个普通的点对点消息到频道的地址。</p><p>频道是实验性的且不受到监管。</p></body></html> + + + + Chan bitmessage address: + 频道的地址: + + + + regenerateAddressesDialog + + + Regenerate Existing Addresses + 重新生成已经存在的地址 + + + + Regenerate existing addresses + 重新生成已经存在的地址 + + + + Passphrase + 密钥 + + + + Number of addresses to make based on your passphrase: + 您想要要使用这个密钥生成的地址数: + + + + Address version number: + 地址版本号: + + + + Stream number: + 节点流序号: + + + + 1 + 1 + + + + Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter + 花费数分钟的计算使地址短1-2个字母 + + + + You must check (or not check) this box just like you did (or didn't) when you made your addresses the first time. + 这个选项需要和您第一次生成的时候相同。 + + + + If you have previously made deterministic addresses but lost them due to an accident (like hard drive failure), you can regenerate them here. If you used the random number generator to make your addresses then this form will be of no use to you. + 如果您之前创建了静态地址,但是因为一些意外失去了它们(比如硬盘坏了),您可以在这里将他们再次生成。如果您使用随机数来生成的地址的话,那么这个表格对您没有帮助。 + + + + settingsDialog + + + Settings + 设置 + + + + Start Bitmessage on user login + 在用户登录时启动比特信 + + + + Start Bitmessage in the tray (don't show main window) + 启动比特信到托盘 (不要显示主窗口) + + + + Minimize to tray + 最小化到托盘 + + + + Show notification when message received + 在收到消息时提示 + + + + Run in Portable Mode + 以便携方式运行 + + + + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. + 在便携模式下, 消息和配置文件和程序保存在同一个目录而不是通常的程序数据文件夹。 这使在U盘中允许比特信很方便。 + + + + Willingly include unencrypted destination address when sending to a mobile device + It seems that this func is still at dev when translation was done. + 愿意在发送到手机时使用不加密的目标地址 + + + + Use Identicons + 用户身份 + + + + Interface Language + 界面语言 + + + + System Settings + system + 系统设置 + + + + English + en + + + + + Esperanto + eo + + + + + Français + fr + + + + + Deutsch + de + + + + + Españl + es + + + + + русский язык + ru + + + + + Norsk + no + + + + + Pirate English + en_pirate + + + + + Other (set in keys.dat) + other + 其他(在keys.dat中设置) + + + + User Interface + 用户界面 + + + + Listening port + 监听端口 + + + + Listen for connections on port: + 监听连接于端口: + + + + Proxy server / Tor + 代理服务器 / Tor + + + + Type: + 类型: + + + + Server hostname: + 服务器主机名: + + + + Port: + 端口: + + + + Authentication + 认证 + + + + Username: + 用户名: + + + + Pass: + 密码: + + + + Listen for incoming connections when using proxy + 在使用代理时仍然监听入站连接 + + + + none + + + + + SOCKS4a + SOCKS4a + + + + SOCKS5 + SOCKS5 + + + + Network Settings + 网络设置 + + + + Total difficulty: + 总难度: + + + + The 'Total difficulty' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work. + “总难度”影响发送者所需要的做工总数。当这个值翻倍时,做工的总数也翻倍。 + + + + Small message difficulty: + 小消息难度: + + + + When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. + 当一个人向您发送消息的时候, 他们的电脑必须先做工。这个难度的默认值是1,您可以在创建新的地址前提高这个值。任何新创建的地址都会要求更高的做工量。这里有一个例外,当您将您的朋友添加到地址本的时候,比特信将自动提示他们,当他们下一次向您发送的时候,他们需要的做功量将总是1. + + + + The 'Small message difficulty' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn't really affect large messages. + “小消息困难度”几乎仅影响发送消息。当这个值翻倍时,发小消息时做工的总数也翻倍,但是并不影响大的消息。 + + + + Demanded difficulty + 要求的难度 + + + + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. + 你可以在这里设置您所愿意接受的发送消息的最大难度。0代表接受任何难度。 + + + + Maximum acceptable total difficulty: + 最大接受难度: + + + + Maximum acceptable small message difficulty: + 最大接受的小消息难度: + + + + Max acceptable difficulty + 最大可接受难度 + + + + <html><head/><body><p>Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to <span style=" font-style:italic;">test. </span></p><p>(Getting your own Bitmessage address into Namecoin is still rather difficult).</p><p>Bitmessage can use either namecoind directly or a running nmcontrol instance.</p></body></html> + <html><head/><body><p>比特信可以利用基于比特币的Namecoin让地址更加友好。比如除了告诉您的朋友您的长长的比特信地址,您还可以告诉他们发消息给 <span style=" font-style:italic;">test. </span></p><p>把您的地址放入Namecoin还是相当的难的.</p><p>比特信可以不但直接连接到namecoin守护程序或者连接到运行中的nmcontrol实例.</p></body></html> + + + + Host: + 主机名: + + + + Password: + 密码: + + + + Test + 测试 + + + + Connect to: + 连接到: + + + + Namecoind + + + + + NMControl + + + + + Namecoin integration + Namecoin整合 + + + + <html><head/><body><p>By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.</p><p>Leave these input fields blank for the default behavior. </p></body></html> + <html><head/><body><p>您发给他们的消息默认会在网络上保存两天,之后比特信会再重发一次. 重发时间会随指数上升; 消息会在5, 10, 20... 天后重发并以此类推. 直到收到收件人的回执. 你可以在这里改变这一行为,让比特信在尝试一段时间后放弃.</p><p>留空意味着默认行为. </p></body></html> + + + + Give up after + + + + + and + + + + + days + + + + + months. + 月后放弃。 + + + + Resends Expire + 重发超时 + + +