From 2a1b6dd86d92e1c7272e4b5713996d4daac13ca4 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 13 Nov 2013 22:44:42 -0500 Subject: [PATCH 01/55] some initial objectProcessorThread work --- src/class_receiveDataThread.py | 42 +++++++++++++--------------------- src/shared.py | 17 ++++++++++++++ 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index b1a62261..01d6428a 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -158,7 +158,7 @@ class receiveDataThread(threading.Thread): elif remoteCommand == 'getdata\x00\x00\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: self.recgetdata(self.data[24:self.payloadLength + 24]) elif remoteCommand == 'msg\x00\x00\x00\x00\x00\x00\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: - self.recmsg(self.data[24:self.payloadLength + 24]) + self.recmsg(self.data[:self.payloadLength + 24]) elif remoteCommand == 'broadcast\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: self.recbroadcast(self.data[24:self.payloadLength + 24]) elif remoteCommand == 'ping\x00\x00\x00\x00\x00\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: @@ -221,19 +221,6 @@ class receiveDataThread(threading.Thread): 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' @@ -342,7 +329,7 @@ class receiveDataThread(threading.Thread): 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): + if not shared.isProofOfWorkSufficient(data): print 'Proof of work in broadcast message insufficient.' return readPosition = 8 # bypass the nonce @@ -858,13 +845,14 @@ class receiveDataThread(threading.Thread): # We have received a msg message. def recmsg(self, data): + readPosition = 24 # bypass the network header self.messageProcessingStartTime = time.time() # First we must check to make sure the proof of work is sufficient. - if not self.isProofOfWorkSufficient(data): + if not shared.isProofOfWorkSufficient(data[readPosition:readPosition+10]): print 'Proof of work in msg message insufficient.' return - readPosition = 8 + readPosition += 8 # bypass the POW nonce embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) # This section is used for the transition from 32 bit time to 64 bit @@ -905,12 +893,15 @@ class receiveDataThread(threading.Thread): shared.inventorySets[self.streamNumber].add(self.inventoryHash) shared.inventoryLock.release() self.broadcastinv(self.inventoryHash) - shared.numberOfMessagesProcessed += 1 - shared.UISignalQueue.put(( - 'updateNumberOfMessagesProcessed', 'no data')) + #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. + #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. + shared.objectProcessorQueue.put(data) + with shard.printLock: + print 'Size of objectProcessorQueue is:', sys.getsizeof(shared.objectProcessorQueue) # Let us now set lengthOfTimeWeShouldUseToProcessThisMessage. If we # haven't used the specified amount of time, we shall sleep. These @@ -1104,7 +1095,7 @@ class receiveDataThread(threading.Thread): toAddress, 'noncetrialsperbyte') requiredPayloadLengthExtraBytes = shared.config.getint( toAddress, 'payloadlengthextrabytes') - if not self.isProofOfWorkSufficient(encryptedData, requiredNonceTrialsPerByte, requiredPayloadLengthExtraBytes): + if not shared.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. @@ -1283,7 +1274,7 @@ class receiveDataThread(threading.Thread): 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): + if not shared.isProofOfWorkSufficient(data): print 'Proof of work in pubkey message insufficient.' return @@ -1567,10 +1558,9 @@ class receiveDataThread(threading.Thread): # the messages that require it. self.possibleNewPubkey(address = fromAddress) - # We have received a getpubkey message def recgetpubkey(self, data): - if not self.isProofOfWorkSufficient(data): + if not shared.isProofOfWorkSufficient(data): print 'Proof of work in getpubkey message insufficient.' return if len(data) < 34: diff --git a/src/shared.py b/src/shared.py index 2404ff15..21d1d3ac 100644 --- a/src/shared.py +++ b/src/shared.py @@ -72,6 +72,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. @@ -277,6 +280,20 @@ def reloadBroadcastSendersForWhichImWatching(): privEncryptionKey = doubleHashOfAddressData[:32] MyECSubscriptionCryptorObjects[tag] = highlevelcrypto.makeCryptor(privEncryptionKey.encode('hex')) +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 doCleanShutdown(): global shutdown shutdown = 1 #Used to tell proof of work worker threads to exit. From b3ba1aed688687039c33cb8298d61a8ddb00bbb7 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 13 Nov 2013 22:45:10 -0500 Subject: [PATCH 02/55] some initial objectProcessorThread work --- src/class_objectProcessor.py | 433 +++++++++++++++++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 src/class_objectProcessor.py diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py new file mode 100644 index 00000000..9d8dbcc7 --- /dev/null +++ b/src/class_objectProcessor.py @@ -0,0 +1,433 @@ +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) + + def run(self): + while True: + data = shared.objectProcessorQueue.get() + + remoteCommand = data[4:16] + if remoteCommand == 'msg\x00\x00\x00\x00\x00\x00\x00\x00\x00': + self.processmsg(data) + + def processmsg(self, data): + """ + We know that the POW and time are correct as they were checked by the + receiveDataThread. + """ + 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 + 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: + with shared.printLock: + print '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: + 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( + 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 + 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(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 != []: + 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: + 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 = (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. + 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. + shared.objectProcessorQueue.put(ackData) + # 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) + + # 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', '')) + + 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 + readPosition = 24 # bypass the network header + if not shared.isProofOfWorkSufficient(ackData[readPosition:readPosition+10]): + print 'Proof of work in msg message insufficient.' + return + + readPosition += 8 # bypass the POW 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 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 not streamNumberAsClaimedByMsg in shared.streamsInWhichIAmParticipating: + print 'The stream number encoded in this msg (' + str(streamNumberAsClaimedByMsg) + ') message does not match a 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 + ################## + 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 \ No newline at end of file From be6b3961b76d6cc2eb6405032e400bdd0f7f5e3f Mon Sep 17 00:00:00 2001 From: Erwin van Eyk Date: Fri, 15 Nov 2013 16:29:11 +0100 Subject: [PATCH 03/55] fix #558 --- src/translations/bitmessage_de.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index 9eb718c3..91fcfd05 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -381,7 +381,7 @@ Es ist wichtig, dass Sie diese Datei sichern. Möchten Sie die datei jetzt öffn From - Von + Von From 380a8e717ed5f46b881946e35c658ff41eff0eaa Mon Sep 17 00:00:00 2001 From: flaskevann Date: Sat, 16 Nov 2013 05:01:02 +0100 Subject: [PATCH 04/55] Create bitmessage_no.ts Norwegian translation for Bitmessage. Everything is translated, including the "unfinished" and "obsolete" parts (better safe then sorry). (I used the french files as template) --- src/translations/bitmessage_no.ts | 1558 +++++++++++++++++++++++++++++ 1 file changed, 1558 insertions(+) create mode 100644 src/translations/bitmessage_no.ts diff --git a/src/translations/bitmessage_no.ts b/src/translations/bitmessage_no.ts new file mode 100644 index 00000000..7ba4aa75 --- /dev/null +++ b/src/translations/bitmessage_no.ts @@ -0,0 +1,1558 @@ + + + + + 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 meldinger' sendt av andre brukere. Meldingene vil vises i din innboks. Adressene her vil overstyre de på 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 on 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 on 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 endringer lagres.) + + + + 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 endringer lagres.) + + + + 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 abonnere på samme adresse flere ganger. + + + + 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 + + + + 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;">Pros:<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;">Cons:<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 genererer 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) + + + + NewSubscriptionDialog + + + Add new entry + Legg til ny oppføring + + + + Label + Etikett + + + + Address + Adresse + + + + 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 + Kopibeskyttet © 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 + + + + 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 Wiki: + + + + iconGlossaryDialog + + + Icon Glossary + Ikonoversikt + + + + You have no connections with other peers. + Du har ingen tilkoblinger til likesinnede. + + + + 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 en likesinnet, 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 i innstillingene) + + + + You do have connections with other peers and your firewall is correctly configured. + Du har aktive tilkoblinger til likesinnede og riktig konfigurert brannmur. + + + + newChanDialog + + + Dialog + Dialog + + + + 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. + + + + 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 annen 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 + + + From a1e67da794add12b66bc5d4af8895ca3b6f4580b Mon Sep 17 00:00:00 2001 From: flaskevann Date: Sun, 17 Nov 2013 08:07:41 +0100 Subject: [PATCH 05/55] Create bitmessage_no.pro A simple .pro file like the other languages have, with correct name for the norwegian translation file. (I guess the .qm file also is needed, but I could not make heads or tails of it) --- src/translations/bitmessage_no.pro | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/translations/bitmessage_no.pro diff --git a/src/translations/bitmessage_no.pro b/src/translations/bitmessage_no.pro new file mode 100644 index 00000000..983c375e --- /dev/null +++ b/src/translations/bitmessage_no.pro @@ -0,0 +1,33 @@ +SOURCES = ../addresses.py\ + ../bitmessagemain.py\ + ../class_addressGenerator.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/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 From 45a0046e7d01bd4f3acb5810a07bfed30693d74f Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 20 Nov 2013 01:29:37 -0500 Subject: [PATCH 06/55] completed work on objectProcessorThread --- src/bitmessagemain.py | 7 + src/class_objectProcessor.py | 1202 ++++++++++++++++++++++----- src/class_receiveDataThread.py | 1410 +------------------------------- src/class_singleWorker.py | 4 +- src/shared.py | 285 ++++++- 5 files changed, 1296 insertions(+), 1612 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index c05b724f..19dfcf12 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -29,6 +29,7 @@ from helper_sql import * from class_sqlThread import * from class_singleCleaner import * from class_singleWorker import * +from class_objectProcessor import * from class_outgoingSynSender import * from class_singleListener import * from class_addressGenerator import * @@ -47,6 +48,7 @@ if sys.platform == 'darwin': 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) @@ -980,6 +982,11 @@ class Main: singleWorkerThread.daemon = True # close the main program even if there are threads left singleWorkerThread.start() + # Start the thread that calculates POWs + objectProcessorThread = objectProcessor() + objectProcessorThread.daemon = True # close the main program even if there are threads left + objectProcessorThread.start() + # Start the SQL thread sqlLookup = sqlThread() sqlLookup.daemon = False # DON'T close the main program even if there are threads left. The closeEvent should command this thread to exit gracefully. diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index 9d8dbcc7..3a389833 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -30,18 +30,338 @@ class objectProcessor(threading.Thread): def run(self): while True: - data = shared.objectProcessorQueue.get() + objectType, data = shared.objectProcessorQueue.get() - remoteCommand = data[4:16] - if remoteCommand == 'msg\x00\x00\x00\x00\x00\x00\x00\x00\x00': + if objectType == 'getpubkey': + self.processgetpubkey(data) + elif objectType == 'pubkey': + self.processpubkey(data) + elif objectType == 'msg': self.processmsg(data) + elif objectType == 'broadcast': + self.processbroadcast(data) + 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. + #print 'objectProcessorQueueSize:', shared.objectProcessorQueueSize + + 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: + 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. Ignoring.\n') + return + if decodeAddress(myAddress)[2] != streamNumber: + with shared.printLock: + sys.stderr.write( + '(Within the recgetpubkey 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.\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(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)) + + 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: + 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] # 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: + 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 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')): + 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) + + # Display timing data + timeRequiredToProcessPubkey = time.time( + ) - pubkeyProcessingStartTime + with shared.printLock: + print 'Time required to process this pubkey:', timeRequiredToProcessPubkey + def processmsg(self, data): - """ - We know that the POW and time are correct as they were checked by the - receiveDataThread. - """ - readPosition = 8 + 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 @@ -90,46 +410,440 @@ class objectProcessor(threading.Thread): 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.' + print 'Length of time program spent failing to decrypt this message:', time.time() - messageProcessingStartTime, 'seconds.' + 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: + 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 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 != []: + 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: + 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 = (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 + 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 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: - # 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.' + readPosition += 4 + + 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 - sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - readPosition += sendersAddressVersionNumberLength - if sendersAddressVersionNumber == 0: - print 'Cannot understand sendersAddressVersionNumber = 0. Ignoring message.' + 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() - 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 = 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 = (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 + with shared.printLock: + print 'Time spent processing this interesting broadcast:', 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 + 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() - messageProcessingStartTime, 'seconds.' + return - if sendersAddressVersionNumber > 4: - print 'Sender\'s address version number', sendersAddressVersionNumber, 'not yet supported. Ignoring message.' + # 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 - if len(decryptedData) < 170: - print 'Length of the unencrypted data is unreasonably short. Sanity check failed. Ignoring message.' + 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 - sendersStreamNumber, sendersStreamNumberLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) - if sendersStreamNumber == 0: - print 'sender\'s stream number is 0. Ignoring message.' - return - readPosition += sendersStreamNumberLength + readPosition += sendersStreamLength behaviorBitfield = decryptedData[readPosition:readPosition + 4] readPosition += 4 - pubSigningKey = '\x04' + decryptedData[ - readPosition:readPosition + 64] + sendersPubSigningKey = '\x04' + \ + decryptedData[readPosition:readPosition + 64] readPosition += 64 - pubEncryptionKey = '\x04' + decryptedData[ - readPosition:readPosition + 64] + sendersPubEncryptionKey = '\x04' + \ + decryptedData[readPosition:readPosition + 64] readPosition += 64 - if sendersAddressVersionNumber >= 3: + if sendersAddressVersion >= 3: requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += varintLength @@ -138,131 +852,81 @@ class objectProcessor(threading.Thread): 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') + 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 - readPosition += 20 messageEncodingType, messageEncodingTypeLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) + decryptedData[readPosition:readPosition + 9]) + if messageEncodingType == 0: + return readPosition += messageEncodingTypeLength messageLength, messageLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) + decryptedData[readPosition:readPosition + 9]) 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 + readPositionAtBottomOfMessage = readPosition signatureLength, signatureLengthLength = decodeVarint( - decryptedData[readPosition:readPosition + 10]) + decryptedData[readPosition:readPosition + 9]) readPosition += signatureLengthLength signature = decryptedData[ readPosition:readPosition + signatureLength] try: - if not highlevelcrypto.verify(decryptedData[:positionOfBottomOfAckData], signature, pubSigningKey.encode('hex')): + 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 - 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.' + # verify passed - # 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(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 != []: - with shared.printLock: - print 'Message ignored because address is in blacklist.' + 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()) - 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: - toLabel = shared.config.get(toAddress, 'label') - if toLabel == '': - toLabel = toAddress + 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. 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) + 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 = '' - shared.UISignalQueue.put(('displayNewInboxMessage', ( - inventoryHash, toAddress, fromAddress, subject, body))) + 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 @@ -274,52 +938,158 @@ class objectProcessor(threading.Thread): except: apiNotifyPath = '' if apiNotifyPath != '': - call([apiNotifyPath, "newMessage"]) + call([apiNotifyPath, "newBroadcast"]) - # 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. - shared.objectProcessorQueue.put(ackData) # 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) + print 'Time spent processing this interesting broadcast:', 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: + 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 = (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 + with shared.printLock: + print 'Time spent processing this interesting broadcast:', 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 @@ -352,7 +1122,7 @@ class objectProcessor(threading.Thread): ripe) shared.workerQueue.put(('sendmessage', '')) - def isAckDataValid(self, ackData): + def ackDataHasAVaildHeader(self, ackData): if len(ackData) < 24: print 'The length of ackData is unreasonably short. Not sending ackData.' return False @@ -363,48 +1133,16 @@ class objectProcessor(threading.Thread): 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': + if ackData[20:24] != hashlib.sha512(ackData[24:]).digest()[0:4]: # test the checksum in the message. + print '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 - readPosition = 24 # bypass the network header - if not shared.isProofOfWorkSufficient(ackData[readPosition:readPosition+10]): - print 'Proof of work in msg message insufficient.' - return - - readPosition += 8 # bypass the POW 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 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 not streamNumberAsClaimedByMsg in shared.streamsInWhichIAmParticipating: - print 'The stream number encoded in this msg (' + str(streamNumberAsClaimedByMsg) + ') message does not match a 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 - ################## return True def decodeType2Message(self, message): @@ -430,4 +1168,20 @@ class objectProcessor(threading.Thread): if '[' + mailingListName + ']' in subject: return subject else: - return '[' + mailingListName + '] ' + subject \ No newline at end of file + 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_receiveDataThread.py b/src/class_receiveDataThread.py index 01d6428a..0c4d5109 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): @@ -56,8 +56,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 +69,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 +88,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 +110,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 +146,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: @@ -158,7 +154,7 @@ class receiveDataThread(threading.Thread): elif remoteCommand == 'getdata\x00\x00\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: self.recgetdata(self.data[24:self.payloadLength + 24]) elif remoteCommand == 'msg\x00\x00\x00\x00\x00\x00\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: - self.recmsg(self.data[:self.payloadLength + 24]) + self.recmsg(self.data[24:self.payloadLength + 24]) elif remoteCommand == 'broadcast\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: self.recbroadcast(self.data[24:self.payloadLength + 24]) elif remoteCommand == 'ping\x00\x00\x00\x00\x00\x00\x00\x00' and self.connectionIsOrWasFullyEstablished: @@ -191,7 +187,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,8 +213,6 @@ 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() @@ -254,8 +248,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)) @@ -328,71 +321,19 @@ class receiveDataThread(threading.Thread): # 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 shared.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 @@ -407,505 +348,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): - readPosition = 24 # bypass the network header self.messageProcessingStartTime = time.time() - # First we must check to make sure the proof of work is sufficient. - if not shared.isProofOfWorkSufficient(data[readPosition:readPosition+10]): - print 'Proof of work in msg message insufficient.' - return - readPosition += 8 # bypass the POW nonce - 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. - shared.objectProcessorQueue.put(data) - with shard.printLock: - print 'Size of objectProcessorQueue is:', sys.getsizeof(shared.objectProcessorQueue) - - # 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 @@ -920,422 +380,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 shared.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 shared.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 - \ @@ -1343,342 +394,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 shared.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 @@ -1702,7 +418,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 @@ -1825,12 +540,12 @@ class receiveDataThread(threading.Thread): # 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 = [] @@ -1853,13 +568,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: @@ -1869,7 +582,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 @@ -1882,7 +594,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: @@ -1892,7 +603,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 @@ -1945,37 +655,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 = {} @@ -2079,7 +758,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]) @@ -2103,7 +781,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. @@ -2116,7 +793,6 @@ 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))) @@ -2148,7 +824,6 @@ class receiveDataThread(threading.Thread): 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') @@ -2156,11 +831,6 @@ class receiveDataThread(threading.Thread): # if not 'Bad file descriptor' in err: with shared.printLock: print 'sock.sendall error:', err - - # cf - # 83 - # e1 - # 35 self.verackSent = True if self.verackReceived: self.connectionFullyEstablished() diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 59d72ac3..a4f72993 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -494,7 +494,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 +508,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 diff --git a/src/shared.py b/src/shared.py index 21d1d3ac..bddd8410 100644 --- a/src/shared.py +++ b/src/shared.py @@ -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. @@ -92,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 = '' @@ -104,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 @@ -113,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 @@ -132,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]: @@ -210,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 @@ -220,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 "" @@ -281,14 +281,13 @@ def reloadBroadcastSendersForWhichImWatching(): MyECSubscriptionCryptorObjects[tag] = highlevelcrypto.makeCryptor(privEncryptionKey.encode('hex')) def isProofOfWorkSufficient( - self, data, nonceTrialsPerByte=0, payloadLengthExtraBytes=0): - if nonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte: - nonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte - if payloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes: - payloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes + 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]) # print 'POW:', POW @@ -413,8 +412,8 @@ 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: @@ -448,7 +447,7 @@ def decryptAndCheckPubkeyPayload(payload, address): 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] @@ -493,5 +492,259 @@ 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): + print '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 + + streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = decodeVarint( + data[readPosition:readPosition + 9]) + if not streamNumberAsClaimedByMsg in streamsInWhichIAmParticipating: + print 'The streamNumber', streamNumberAsClaimedByMsg, 'isn\'t one we are interested in.' + return + readPosition += streamNumberAsClaimedByMsgLength + inventoryHash = calculateInventoryHash(data) + shared.numberOfInventoryLookupsPerformed += 1 + inventoryLock.acquire() + if inventoryHash in inventory: + print 'We have already received this msg message. Ignoring.' + inventoryLock.release() + return + elif isInSqlInventory(inventoryHash): + print '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() + with printLock: + print 'advertising inv with hash:', 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): + 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()) - 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 not streamNumber in streamsInWhichIAmParticipating: + print 'The streamNumber', streamNumber, 'isn\'t one we are interested in.' + return + readPosition += streamNumberLength + + shared.numberOfInventoryLookupsPerformed += 1 + inventoryHash = calculateInventoryHash(data) + inventoryLock.acquire() + if inventoryHash in inventory: + print 'We have already received this getpubkey request. Ignoring it.' + inventoryLock.release() + return + elif isInSqlInventory(inventoryHash): + print '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. + with printLock: + print 'advertising inv with hash:', 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): + 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()) - lengthOfTimeToHoldOnToAllPubkeys: + with 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 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 not streamNumber in streamsInWhichIAmParticipating: + print 'The streamNumber', streamNumber, 'isn\'t one we are interested in.' + 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) + inventoryLock.acquire() + if inventoryHash in inventory: + print 'We have already received this pubkey. Ignoring it.' + inventoryLock.release() + return + elif isInSqlInventory(inventoryHash): + print '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. + with printLock: + print 'advertising inv with hash:', 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): + 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 + + 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()) - 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 + broadcastVersion, broadcastVersionLength = decodeVarint( + data[readPosition:readPosition + 10]) + if broadcastVersion >= 2: + streamNumber, streamNumberLength = decodeVarint(data[ + readPosition + broadcastVersionLength:readPosition + broadcastVersionLength + 10]) + if not streamNumber in streamsInWhichIAmParticipating: + print 'The streamNumber', streamNumber, 'isn\'t one we are interested in.' + return + + shared.numberOfInventoryLookupsPerformed += 1 + inventoryLock.acquire() + inventoryHash = calculateInventoryHash(data) + if inventoryHash in inventory: + print 'We have already received this broadcast object. Ignoring.' + inventoryLock.release() + return + elif isInSqlInventory(inventoryHash): + print '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,'') + inventorySets[streamNumber].add(inventoryHash) + inventoryLock.release() + # This object is valid. Forward it to peers. + with printLock: + print 'advertising inv with hash:', 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 From 01620ec86870062b2d058ec20d3b915679a8d9c9 Mon Sep 17 00:00:00 2001 From: Hans Wolff Date: Mon, 25 Nov 2013 19:00:39 +0000 Subject: [PATCH 07/55] fixed typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Verschlüsselungscode Anforderung" -> "Verschlüsselungscode-Anforderung" "Kennowrtsatz" -> "Kennwortsatz" --- src/translations/bitmessage_de.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index 9eb718c3..3c74888a 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -102,7 +102,7 @@ Encryption key request queued. - Verschlüsselungscode Anforderung steht aus. + Verschlüsselungscode-Anforderung steht aus. @@ -945,7 +945,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> From f1ce821910921bc41c5b215ef5bb5259ac688c52 Mon Sep 17 00:00:00 2001 From: Dmitry Belyaev Date: Sat, 30 Nov 2013 00:42:15 +0400 Subject: [PATCH 08/55] fix AppdataFolder encoding on windows --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index 2404ff15..09306651 100644 --- a/src/shared.py +++ b/src/shared.py @@ -146,7 +146,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: From 44618ffcc2095de10051af7dcb447846768a060d Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 30 Nov 2013 23:15:18 -0500 Subject: [PATCH 09/55] added option to show recent broadcasts when subscribing --- src/bitmessageqt/__init__.py | 119 ++++++++++++++++++---- src/bitmessageqt/addaddressdialog.py | 65 ++++++++++++ src/bitmessageqt/addaddressdialog.ui | 97 ++++++++++++++++++ src/bitmessageqt/newsubscriptiondialog.py | 53 ++++++---- src/bitmessageqt/newsubscriptiondialog.ui | 71 +++++-------- src/shared.py | 13 ++- 6 files changed, 326 insertions(+), 92 deletions(-) create mode 100644 src/bitmessageqt/addaddressdialog.py create mode 100644 src/bitmessageqt/addaddressdialog.ui diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a3bf7ec3..2ae7d00a 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 * @@ -2063,15 +2064,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( @@ -2120,7 +2121,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())) @@ -2130,6 +2131,23 @@ 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 @@ -2368,11 +2386,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. @@ -2386,7 +2404,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) @@ -2394,7 +2412,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: @@ -3465,6 +3483,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): @@ -3473,31 +3525,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/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/shared.py b/src/shared.py index bddd8410..9350fdaf 100644 --- a/src/shared.py +++ b/src/shared.py @@ -708,13 +708,18 @@ def checkAndShareBroadcastWithPeers(data): return broadcastVersion, broadcastVersionLength = decodeVarint( data[readPosition:readPosition + 10]) + readPosition += broadcastVersionLength if broadcastVersion >= 2: - streamNumber, streamNumberLength = decodeVarint(data[ - readPosition + broadcastVersionLength:readPosition + broadcastVersionLength + 10]) + streamNumber, streamNumberLength = decodeVarint(data[readPosition:readPosition + 10]) + readPosition += streamNumberLength if not streamNumber in streamsInWhichIAmParticipating: print 'The streamNumber', streamNumber, 'isn\'t one we are interested in.' return - + if broadcastVersion >= 3: + tag = data[readPosition:readPosition+32] + print 'debugging. in broadcast, tag is:', tag.encode('hex') + else: + tag = '' shared.numberOfInventoryLookupsPerformed += 1 inventoryLock.acquire() inventoryHash = calculateInventoryHash(data) @@ -729,7 +734,7 @@ def checkAndShareBroadcastWithPeers(data): # It is valid. Let's let our peers know about it. objectType = 'broadcast' inventory[inventoryHash] = ( - objectType, streamNumber, data, embeddedTime,'') + objectType, streamNumber, data, embeddedTime, tag) inventorySets[streamNumber].add(inventoryHash) inventoryLock.release() # This object is valid. Forward it to peers. From 3c79b7bf65d303c7fd16dd3814407dc490c1cb43 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sun, 1 Dec 2013 00:45:37 -0500 Subject: [PATCH 10/55] save the tag in the inventory for your own broadcasts also --- src/bitmessageqt/__init__.py | 1 - src/class_singleWorker.py | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 2ae7d00a..df1ea4cf 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2147,7 +2147,6 @@ class MyForm(QtGui.QMainWindow): with shared.objectProcessorQueueSizeLock: shared.objectProcessorQueueSize += len(payload) shared.objectProcessorQueue.put((objectType,payload)) - #### def loadBlackWhiteList(self): # Initialize the Blacklist or Whitelist table diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index a4f72993..62cf8308 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -386,7 +386,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 +419,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 +438,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') From 6d197e97b147834fcaf3f817bcc9b29ab7e54839 Mon Sep 17 00:00:00 2001 From: bikash617 Date: Sun, 1 Dec 2013 13:23:34 +0400 Subject: [PATCH 11/55] populate from field feature implementation --- src/bitmessageqt/__init__.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a3bf7ec3..a4621c75 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2548,6 +2548,11 @@ class MyForm(QtGui.QMainWindow): currentInboxRow = self.ui.tableWidgetInbox.currentRow() toAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( currentInboxRow, 0).data(Qt.UserRole).toPyObject()) + + + toAddressAtCurrentInboxRow_label = str(self.ui.tableWidgetInbox.item( + currentInboxRow, 0).text()) + fromAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( currentInboxRow, 1).data(Qt.UserRole).toPyObject()) msgid = str(self.ui.tableWidgetInbox.item( @@ -2578,8 +2583,27 @@ 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) + + + + AllItems = [str(self.ui.comboBoxSendFrom.itemText(i)) for i in range(self.ui.comboBoxSendFrom.count())] + AllItems_address = [str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) for i in range(self.ui.comboBoxSendFrom.count())] + + isPresent_Label = False + for item in AllItems: + if toAddressAtCurrentInboxRow_label == item: + isPresent_Label = True + break; + if(isPresent_Label == True): + currentIndex = AllItems.index(toAddressAtCurrentInboxRow_label) + self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) + + else: + currentIndex = AllItems_address.index(toAddressAtCurrentInboxRow) + self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) + + + #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( From df7116bd72eafcb3d102245468a6f9439c12e8a5 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 2 Dec 2013 01:35:34 -0500 Subject: [PATCH 12/55] on close, save objectProcessorQueue to disk --- src/bitmessagemain.py | 10 +++++----- src/bitmessageqt/__init__.py | 2 +- src/class_objectProcessor.py | 35 ++++++++++++++++++++++++++++++++++- src/class_sqlThread.py | 23 ++++++++++++++++++----- src/proofofwork.py | 2 +- src/shared.py | 31 ++++++++++++++++++++----------- 6 files changed, 79 insertions(+), 24 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 19dfcf12..6caf0c98 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -982,16 +982,16 @@ class Main: singleWorkerThread.daemon = True # close the main program even if there are threads left singleWorkerThread.start() - # Start the thread that calculates POWs - objectProcessorThread = objectProcessor() - objectProcessorThread.daemon = True # close the main program even if there are threads left - objectProcessorThread.start() - # Start the SQL thread sqlLookup = sqlThread() 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 diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index df1ea4cf..684784b6 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2146,7 +2146,7 @@ class MyForm(QtGui.QMainWindow): objectType = 'broadcast' with shared.objectProcessorQueueSizeLock: shared.objectProcessorQueueSize += len(payload) - shared.objectProcessorQueue.put((objectType,payload)) + shared.objectProcessorQueue.put((objectType,payload)) def loadBlackWhiteList(self): # Initialize the Blacklist or Whitelist table diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index 3a389833..e32d5a6d 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -27,6 +27,23 @@ class objectProcessor(threading.Thread): """ 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: @@ -40,13 +57,29 @@ class objectProcessor(threading.Thread): 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. - #print 'objectProcessorQueueSize:', shared.objectProcessorQueueSize + 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]) 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/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 9350fdaf..0f1f3690 100644 --- a/src/shared.py +++ b/src/shared.py @@ -295,8 +295,12 @@ def isProofOfWorkSufficient( 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...')) @@ -314,13 +318,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) @@ -542,7 +551,7 @@ def checkAndShareMsgWithPeers(data): time.sleep(2) with shared.objectProcessorQueueSizeLock: shared.objectProcessorQueueSize += len(data) - objectProcessorQueue.put((objectType,data)) + objectProcessorQueue.put((objectType,data)) def checkAndSharegetpubkeyWithPeers(data): if not isProofOfWorkSufficient(data): @@ -606,7 +615,7 @@ def checkAndSharegetpubkeyWithPeers(data): time.sleep(2) with shared.objectProcessorQueueSizeLock: shared.objectProcessorQueueSize += len(data) - objectProcessorQueue.put((objectType,data)) + objectProcessorQueue.put((objectType,data)) def checkAndSharePubkeyWithPeers(data): if len(data) < 146 or len(data) > 420: # sanity check @@ -678,7 +687,7 @@ def checkAndSharePubkeyWithPeers(data): time.sleep(2) with shared.objectProcessorQueueSizeLock: shared.objectProcessorQueueSize += len(data) - objectProcessorQueue.put((objectType,data)) + objectProcessorQueue.put((objectType,data)) def checkAndShareBroadcastWithPeers(data): @@ -748,7 +757,7 @@ def checkAndShareBroadcastWithPeers(data): time.sleep(2) with shared.objectProcessorQueueSizeLock: shared.objectProcessorQueueSize += len(data) - objectProcessorQueue.put((objectType,data)) + objectProcessorQueue.put((objectType,data)) helper_startup.loadConfig() From 80932bbab0352a4cbe760b308b6722676595e120 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 6 Dec 2013 01:52:19 -0500 Subject: [PATCH 13/55] fix pubkey signature bug leftover from objectProcessorThread-related-changes --- src/class_objectProcessor.py | 12 ++++++++++-- src/class_singleWorker.py | 19 ++++++++----------- src/shared.py | 2 +- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index e32d5a6d..0a2f2594 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -149,7 +149,7 @@ class objectProcessor(threading.Thread): 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(config.get( + lastPubkeySendTime = int(shared.config.get( myAddress, 'lastpubkeysendtime')) except: lastPubkeySendTime = 0 @@ -298,6 +298,15 @@ class objectProcessor(threading.Thread): 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. print '(within processpubkey) payloadLength less than 350. Sanity check failed.' return @@ -321,7 +330,6 @@ class objectProcessor(threading.Thread): print 'Pubkey decryption was unsuccessful.' return - readPosition = 0 bitfieldBehaviors = decryptedData[readPosition:readPosition + 4] readPosition += 4 diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 62cf8308..e76b75a0 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( @@ -686,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) @@ -726,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) @@ -795,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) @@ -934,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/shared.py b/src/shared.py index 0f1f3690..b78bbd83 100644 --- a/src/shared.py +++ b/src/shared.py @@ -437,7 +437,7 @@ def decryptAndCheckPubkeyPayload(payload, address): print '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:] From fb98cd0fc870c8a86f0595a5ccf1e4c65fc8ec84 Mon Sep 17 00:00:00 2001 From: Jeroen Van Goey Date: Mon, 9 Dec 2013 00:25:07 +0100 Subject: [PATCH 14/55] remove duplicate code There was twice a check for the Python version on OSX but the logging in the second step could never be reached. --- src/bitmessagemain.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 6caf0c98..1f9c4c5f 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -20,8 +20,9 @@ import os 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) + 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) # Classes @@ -41,12 +42,6 @@ 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)) - sys.exit(0) - def connectToStream(streamNumber): shared.streamsInWhichIAmParticipating[streamNumber] = 'no data' selfInitiatedConnections[streamNumber] = {} From 90b0d4c6408ce271e4fd0231e7c8c77bf53f6a8f Mon Sep 17 00:00:00 2001 From: Jeroen Van Goey Date: Mon, 9 Dec 2013 00:32:19 +0100 Subject: [PATCH 15/55] remove duplicate code There was twice a check for the Python version on OSX, but the logging in the second check was never reached --- src/bitmessagemain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 1f9c4c5f..beefabf2 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -20,8 +20,8 @@ import os import sys if sys.platform == 'darwin': if float("{1}.{2}".format(*sys.version_info)) < 7.5: - msg = "You should use python 2.7.5 or greater. Your version: %s", "{0}.{1}.{2}".format(*sys.version_info) - logger.critical(msg) + 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) From 1a489691be7dc9d927744c69f8177b37ff18d4d1 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 16 Dec 2013 00:15:07 -0500 Subject: [PATCH 16/55] added Norwegian translation to UI --- src/bitmessageqt/__init__.py | 2 +- src/bitmessageqt/settings.py | 10 ++++++---- src/bitmessageqt/settings.ui | 5 +++++ src/translations/bitmessage_no.qm | Bin 0 -> 57925 bytes 4 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 src/translations/bitmessage_no.qm diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 684784b6..e63dd025 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3262,7 +3262,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','en_pirate','other'] user_countrycode = str(shared.config.get('bitmessagesettings', 'userlocale')) if user_countrycode in languages: curr_index = languages.index(user_countrycode) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 9b045e38..7c7cb61a 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 Dec 16 00:08:18 2013 +# by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -88,6 +88,7 @@ class Ui_settingsDialog(object): 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 +398,9 @@ 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", "Pirate English", "en_pirate")) + self.languageComboBox.setItemText(9, _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..6d33bbee 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -154,6 +154,11 @@ русский язык + + + norsk + + Pirate English diff --git a/src/translations/bitmessage_no.qm b/src/translations/bitmessage_no.qm new file mode 100644 index 0000000000000000000000000000000000000000..e0b248c617c665bd8db84706ba481c3c4eed76e8 GIT binary patch literal 57925 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 Date: Mon, 16 Dec 2013 00:19:15 -0500 Subject: [PATCH 17/55] added class_objectProcessor.py to the translation .pro files --- src/translations/bitmessage_de.pro | 1 + src/translations/bitmessage_en_pirate.pro | 1 + src/translations/bitmessage_eo.pro | 1 + src/translations/bitmessage_fr.pro | 1 + src/translations/bitmessage_no.pro | 1 + src/translations/bitmessage_ru.pro | 1 + 6 files changed, 6 insertions(+) diff --git a/src/translations/bitmessage_de.pro b/src/translations/bitmessage_de.pro index 1e8fe836..fc4bb566 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\ diff --git a/src/translations/bitmessage_en_pirate.pro b/src/translations/bitmessage_en_pirate.pro index 5b7f27e4..709102ae 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\ diff --git a/src/translations/bitmessage_eo.pro b/src/translations/bitmessage_eo.pro index 7a53b739..5c945f26 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\ diff --git a/src/translations/bitmessage_fr.pro b/src/translations/bitmessage_fr.pro index f2215048..ce57b0da 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\ diff --git a/src/translations/bitmessage_no.pro b/src/translations/bitmessage_no.pro index 983c375e..b362b622 100644 --- a/src/translations/bitmessage_no.pro +++ b/src/translations/bitmessage_no.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\ diff --git a/src/translations/bitmessage_ru.pro b/src/translations/bitmessage_ru.pro index 0370961b..2745c87b 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\ From 98ce6340e81f19719d5be7b0ef57450ef997535d Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 16 Dec 2013 00:32:46 -0500 Subject: [PATCH 18/55] updated all translation .ts files --- src/translations/bitmessage_de.pro | 1 + src/translations/bitmessage_de.ts | 719 ++++++++++++------ src/translations/bitmessage_en_pirate.pro | 1 + src/translations/bitmessage_en_pirate.ts | 888 +++++++++++++--------- src/translations/bitmessage_eo.pro | 1 + src/translations/bitmessage_eo.ts | 760 +++++++++++------- src/translations/bitmessage_fr.pro | 1 + src/translations/bitmessage_fr.ts | 725 ++++++++++++------ src/translations/bitmessage_no.pro | 1 + src/translations/bitmessage_no.ts | 728 ++++++++++++------ src/translations/bitmessage_ru.pro | 1 + src/translations/bitmessage_ru.ts | 675 ++++++++++------ 12 files changed, 2888 insertions(+), 1613 deletions(-) diff --git a/src/translations/bitmessage_de.pro b/src/translations/bitmessage_de.pro index fc4bb566..56e2b4f1 100644 --- a/src/translations/bitmessage_de.pro +++ b/src/translations/bitmessage_de.pro @@ -18,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 91fcfd05..78fb6674 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 on 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. - + Queued. In Warteschlange. - + Message sent. Waiting on 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 - + 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 @@ -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 709102ae..acc712ee 100644 --- a/src/translations/bitmessage_en_pirate.pro +++ b/src/translations/bitmessage_en_pirate.pro @@ -18,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 496b7d64..47427365 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 on their encryption key. Will request it again soon. - + Encryption key request queued. - + Queued. - + Message sent. Waiting on 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 5c945f26..1e9492cd 100644 --- a/src/translations/bitmessage_eo.pro +++ b/src/translations/bitmessage_eo.pro @@ -18,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 461ff559..f79ccc4d 100644 --- a/src/translations/bitmessage_eo.ts +++ b/src/translations/bitmessage_eo.ts @@ -1,208 +1,217 @@ - - + + + 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 on 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 on 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 +220,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 +239,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 +434,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 +600,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 +907,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 +1074,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 +1129,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 +1189,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 +1257,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 +1339,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 ce57b0da..0e56c016 100644 --- a/src/translations/bitmessage_fr.pro +++ b/src/translations/bitmessage_fr.pro @@ -18,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 d68a5990..7426ee15 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 on 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 on 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 index b362b622..d34ad2f9 100644 --- a/src/translations/bitmessage_no.pro +++ b/src/translations/bitmessage_no.pro @@ -18,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_no.ts b/src/translations/bitmessage_no.ts index 7ba4aa75..ddc0844e 100644 --- a/src/translations/bitmessage_no.ts +++ b/src/translations/bitmessage_no.ts @@ -1,55 +1,72 @@ - - + + + 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 @@ -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: 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 meldinger' sendt av andre brukere. Meldingene vil vises i din innboks. Adressene her vil overstyre de på 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 @@ -192,182 +209,182 @@ p, li { white-space: pre-wrap; } 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 on 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 on 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. @@ -376,7 +393,7 @@ It is important that you back up this file. 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 endringer lagres.) @@ -385,102 +402,102 @@ 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. 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 + 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 endringer lagres.) - + 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. @@ -490,77 +507,77 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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. @@ -570,152 +587,152 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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 + 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. + 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 abonnere på samme adresse flere ganger. - + 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 @@ -725,9 +742,8 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil Autentiseringsproblem med SOCKS5: %1 - + Reply - . Svar @@ -790,10 +806,10 @@ Mottakernes krav til vanskelighet: %1 og %2 Work is queued. - Arbeidsoppgave er satt i kø. + Arbeidsoppgave er satt i kø. - + Work is queued. %1 Arbeidsoppgave er satt i kø. %1 @@ -805,185 +821,291 @@ There is no required difficulty for version 2 addresses like this. 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. + 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... + + + + + 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.) + + + + + 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 @@ -1011,7 +1133,7 @@ The 'Random Number' option is selected by default but deterministic ad <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;">Pros:<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;">Cons:<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 @@ -1035,7 +1157,7 @@ The 'Random Number' option is selected by default but deterministic ad Address version number: 3 - Adressetypenummer: 3 + Adressetypenummer: 3 @@ -1092,24 +1214,34 @@ The 'Random Number' option is selected by default but deterministic ad (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 + + SpecialAddressBehaviorDialog @@ -1142,35 +1274,40 @@ The 'Random Number' option is selected by default but deterministic ad aboutDialog - + PyBitmessage PyBitmessage - + version ? versjon ? - + About Om - + Copyright © 2013 Jonathan Warren - Kopibeskyttet © 2013 Jonathan Warren + Kopibeskyttet © 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-2013 Jonathan Warren<br/>Copyright © 2013 The Bitmessage Developers</p></body></html> + + connectDialog @@ -1287,272 +1424,377 @@ The 'Random Number' option is selected by default but deterministic ad 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: + Adressetypenummer: 3 - 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: + + 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'):) + 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 annen 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 + + + + + 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_ru.pro b/src/translations/bitmessage_ru.pro index 2745c87b..5d74581c 100644 --- a/src/translations/bitmessage_ru.pro +++ b/src/translations/bitmessage_ru.pro @@ -18,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 3be907d0..92d4662b 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 on their encryption key. Will request it again soon. Ожидаем ключ шифрования от Вашего собеседника. Запрос будет повторен через некоторое время. - + Encryption key request queued. Запрос ключа шифрования поставлен в очередь. - + Queued. В очереди. - + Message sent. Waiting on 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 + + From 2ca0fed2272335c700ec9b258eee72c2b2b53864 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 25 Dec 2013 00:46:14 -0500 Subject: [PATCH 19/55] fix error during fixing of merge conflict --- src/translations/bitmessage_en_pirate.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/translations/bitmessage_en_pirate.ts b/src/translations/bitmessage_en_pirate.ts index 223c4b8b..2e719bf4 100644 --- a/src/translations/bitmessage_en_pirate.ts +++ b/src/translations/bitmessage_en_pirate.ts @@ -121,8 +121,7 @@ - - Waiting for their encryption key. Will request it again soon. + Waiting for their encryption key. Will request it again soon. From 3c00a443ae9db620b5ece38f48b6b685f24c3f4f Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 25 Dec 2013 01:30:39 -0500 Subject: [PATCH 20/55] added error handling to previous commit: populate 'from' combo box when replying --- src/bitmessageqt/__init__.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e4901139..2b0ad2cc 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2655,24 +2655,29 @@ class MyForm(QtGui.QMainWindow): - AllItems = [str(self.ui.comboBoxSendFrom.itemText(i)) for i in range(self.ui.comboBoxSendFrom.count())] - AllItems_address = [str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) for i in range(self.ui.comboBoxSendFrom.count())] - + listOfLabelsInComboBoxSendFrom = [str(self.ui.comboBoxSendFrom.itemText(i)) for i in range(self.ui.comboBoxSendFrom.count())] + listOfAddressesInComboBoxSendFrom = [str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) for i in range(self.ui.comboBoxSendFrom.count())] + """ isPresent_Label = False - for item in AllItems: + for item in listOfLabelsInComboBoxSendFrom: if toAddressAtCurrentInboxRow_label == item: isPresent_Label = True break; - if(isPresent_Label == True): - currentIndex = AllItems.index(toAddressAtCurrentInboxRow_label) + """ + #if isPresent_Label: + if toAddressAtCurrentInboxRow_label in listOfLabelsInComboBoxSendFrom: + currentIndex = listOfLabelsInComboBoxSendFrom.index(toAddressAtCurrentInboxRow_label) self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) else: - currentIndex = AllItems_address.index(toAddressAtCurrentInboxRow) - self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) - - - #self.ui.comboBoxSendFrom.setCurrentIndex(0) + try: + currentIndex = listOfAddressesInComboBoxSendFrom.index(toAddressAtCurrentInboxRow) + self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) + except: + # The toAddressAtCurrentInboxRow isn't in our combo box which can happen if + # we are replying to a broadcast address or if the address is disabled. + 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( From 9fdff73ee151df91b4e2dcf73d500f07922c2f6e Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 25 Dec 2013 01:48:01 -0500 Subject: [PATCH 21/55] simplify last commit --- src/bitmessageqt/__init__.py | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 2b0ad2cc..d43a22e9 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2617,10 +2617,6 @@ class MyForm(QtGui.QMainWindow): currentInboxRow = self.ui.tableWidgetInbox.currentRow() toAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( currentInboxRow, 0).data(Qt.UserRole).toPyObject()) - - - toAddressAtCurrentInboxRow_label = str(self.ui.tableWidgetInbox.item( - currentInboxRow, 0).text()) fromAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( currentInboxRow, 1).data(Qt.UserRole).toPyObject()) @@ -2653,30 +2649,12 @@ class MyForm(QtGui.QMainWindow): print 'original sent to a chan. Setting the to address in the reply to the chan address.' self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow)) - - - listOfLabelsInComboBoxSendFrom = [str(self.ui.comboBoxSendFrom.itemText(i)) for i in range(self.ui.comboBoxSendFrom.count())] listOfAddressesInComboBoxSendFrom = [str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) for i in range(self.ui.comboBoxSendFrom.count())] - """ - isPresent_Label = False - for item in listOfLabelsInComboBoxSendFrom: - if toAddressAtCurrentInboxRow_label == item: - isPresent_Label = True - break; - """ - #if isPresent_Label: - if toAddressAtCurrentInboxRow_label in listOfLabelsInComboBoxSendFrom: - currentIndex = listOfLabelsInComboBoxSendFrom.index(toAddressAtCurrentInboxRow_label) + if toAddressAtCurrentInboxRow in listOfAddressesInComboBoxSendFrom: + currentIndex = listOfAddressesInComboBoxSendFrom.index(toAddressAtCurrentInboxRow) self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) - else: - try: - currentIndex = listOfAddressesInComboBoxSendFrom.index(toAddressAtCurrentInboxRow) - self.ui.comboBoxSendFrom.setCurrentIndex(currentIndex) - except: - # The toAddressAtCurrentInboxRow isn't in our combo box which can happen if - # we are replying to a broadcast address or if the address is disabled. - self.ui.comboBoxSendFrom.setCurrentIndex(0) + 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:']: From 5fe54f95560b022b85c48432a28d2cd557d5781a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eino=20M=C3=A4kitalo?= Date: Mon, 30 Dec 2013 02:49:27 +0200 Subject: [PATCH 22/55] Cleaning imports and separating API to another file. PyLint fixes for main file --- src/bitmessageapi.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/bitmessageapi.py diff --git a/src/bitmessageapi.py b/src/bitmessageapi.py new file mode 100644 index 00000000..bd17f924 --- /dev/null +++ b/src/bitmessageapi.py @@ -0,0 +1,4 @@ +# Copyright (c) 2012 Jonathan Warren +# Copyright (c) 2012 The Bitmessage developers +# Moved ugly API stuff from main program to here /EM + From b0c582605fb52aeedd2f7e1afb3723088a47848d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eino=20M=C3=A4kitalo?= Date: Mon, 30 Dec 2013 02:53:44 +0200 Subject: [PATCH 23/55] Cleaning imports and separating API to another file. PyLint fixes for main file --- src/bitmessageapi.py | 903 +++++++++++++++++++++++++++++++++++++++++ src/bitmessagemain.py | 920 ++---------------------------------------- 2 files changed, 932 insertions(+), 891 deletions(-) create mode 100644 src/bitmessageapi.py diff --git a/src/bitmessageapi.py b/src/bitmessageapi.py new file mode 100644 index 00000000..a96ba041 --- /dev/null +++ b/src/bitmessageapi.py @@ -0,0 +1,903 @@ +# Copyright (c) 2012 Jonathan Warren +# Copyright (c) 2012 The Bitmessage developers +# Moved API stuff from main program to here /EM + +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) + data # this is totally meaningless row? + 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 b9500adb..08150c69 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -11,11 +11,10 @@ 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 + # OSX python version check import sys if sys.platform == 'darwin': @@ -24,24 +23,38 @@ if sys.platform == 'darwin': print "Your version: {0}.{1}.{2}".format(*sys.version_info) sys.exit(0) +from SimpleXMLRPCServer import SimpleXMLRPCServer +from bitmessageapi import MySimpleXMLRPCRequestHandler + +import shared +from helper_sql import sqlQuery +import threading + + # Classes -from helper_sql import * -from class_sqlThread import * -from class_singleCleaner import * -from class_singleWorker import * -from class_objectProcessor import * -from class_outgoingSynSender import * -from class_singleListener import * -from class_addressGenerator import * +#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 proofofwork +import helper_generic +#import proofofwork -str_chan = '[chan]' +from subprocess import call +import time -import sys + +#TODO: done twice - quite paranoid 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)) @@ -65,883 +78,7 @@ def connectToStream(streamNumber): 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): @@ -967,7 +104,7 @@ class Main: def start(self, daemon=False): shared.daemon = daemon # is the application already running? If yes then exit. - thisapp = singleton.singleinstance() + singleton.singleinstance() signal.signal(signal.SIGINT, helper_generic.signal_handler) # signal.signal(signal.SIGINT, signal.SIG_DFL) @@ -1051,6 +188,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 @@ -1065,4 +203,4 @@ if __name__ == "__main__": # So far, the creation of and management of the Bitmessage protocol and this # client is a one-man operation. Bitcoin tips are quite appreciated. -# 1H5XaDA6fYENLbknwZyjiYXYPQaFjjLX2u +# 1H5XaDA6fYENLbknwZyjiYXYPQaFjjLX2u \ No newline at end of file From 61389b64aaf7f680ea4dae60c63c2d582409a9bb Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sun, 29 Dec 2013 22:36:23 -0500 Subject: [PATCH 24/55] fix #590 --- src/bitmessageqt/__init__.py | 1 - src/class_outgoingSynSender.py | 12 ++++-- src/class_receiveDataThread.py | 69 +++++++--------------------------- src/class_sendDataThread.py | 43 +++++++++++++-------- src/class_singleListener.py | 5 ++- 5 files changed, 53 insertions(+), 77 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index d43a22e9..e79af31d 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2617,7 +2617,6 @@ class MyForm(QtGui.QMainWindow): currentInboxRow = self.ui.tableWidgetInbox.currentRow() toAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( currentInboxRow, 0).data(Qt.UserRole).toPyObject()) - fromAddressAtCurrentInboxRow = str(self.ui.tableWidgetInbox.item( currentInboxRow, 1).data(Qt.UserRole).toPyObject()) msgid = str(self.ui.tableWidgetInbox.item( 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 0c4d5109..bdad9996 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -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. @@ -218,13 +221,7 @@ class receiveDataThread(threading.Thread): 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): @@ -309,13 +306,7 @@ 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 @@ -462,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 @@ -531,12 +517,7 @@ 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 @@ -735,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 @@ -794,8 +766,7 @@ class receiveDataThread(threading.Thread): 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()) @@ -810,27 +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 + 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_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: From 40a727ebd22d191d330819e0a4020cf646678195 Mon Sep 17 00:00:00 2001 From: flaskevann Date: Fri, 3 Jan 2014 21:19:27 +0100 Subject: [PATCH 25/55] Update bitmessage_no.ts Fixed all the special chars and translated the new lines at the bottom. --- src/translations/bitmessage_no.ts | 276 +++++++++++++++--------------- 1 file changed, 138 insertions(+), 138 deletions(-) diff --git a/src/translations/bitmessage_no.ts b/src/translations/bitmessage_no.ts index 91b87901..9411ccb2 100644 --- a/src/translations/bitmessage_no.ts +++ b/src/translations/bitmessage_no.ts @@ -5,7 +5,7 @@ Add new entry - Legg til ny oppføring + Legg til ny oppføring @@ -96,7 +96,7 @@ p, li { white-space: pre-wrap; } Broadcast to everyone who is subscribed to your address - Kringkast til alle som abonnerer på din adresse + Kringkast til alle som abonnerer på din adresse @@ -106,7 +106,7 @@ p, li { white-space: pre-wrap; } 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. + 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. @@ -136,7 +136,7 @@ p, li { white-space: pre-wrap; } Stream - Strøm + Strøm @@ -146,7 +146,7 @@ p, li { white-space: pre-wrap; } 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 meldinger' sendt av andre brukere. Meldingene vil vises i din innboks. Adressene her vil overstyre de på svartelistefanen. + Her kan du abonnere på 'kringkastede meldinger' sendt av andre brukere. Meldingene vil vises i din innboks. Adressene her vil overstyre de på svartelistefanen. @@ -166,12 +166,12 @@ p, li { white-space: pre-wrap; } 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. + 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 + Legg til ny oppføring @@ -186,12 +186,12 @@ p, li { white-space: pre-wrap; } Use a Blacklist (Allow all incoming messages except those on the Blacklist) - Bruk svarteliste (tillat beskjeder fra alle adresser unntatt de på svartelisten) + 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) + Bruk hviteliste (blokker beskjeder fra alle adresser unntatt de på hvitelisten) @@ -201,7 +201,7 @@ p, li { white-space: pre-wrap; } Stream Number - Strømnummer + Strømnummer @@ -216,7 +216,7 @@ p, li { white-space: pre-wrap; } Since startup at asdf: - Siden oppstart på asdf: + Siden oppstart på asdf: @@ -226,7 +226,7 @@ p, li { white-space: pre-wrap; } Processed 0 public key. - Har bearbeidet 0 offentlige nøkler. + Har bearbeidet 0 offentlige nøkler. @@ -256,12 +256,12 @@ p, li { white-space: pre-wrap; } Import keys - Importer inn nøkler + Importer inn nøkler Manage keys - Administrer nøkler + Administrer nøkler @@ -321,7 +321,7 @@ p, li { white-space: pre-wrap; } Processed %1 public keys. - Bearbeidet %1 offentlige nøkler. + Bearbeidet %1 offentlige nøkler. @@ -331,42 +331,42 @@ p, li { white-space: pre-wrap; } Waiting for their encryption key. Will request it again soon. - Venter på krypteringsnøkkel. Sender straks en ny forespørsel. + Venter på krypteringsnøkkel. Sender straks en ny forespørsel. Encryption key request queued. - Forespørsel for å finne krypteringsnøkkel er satt i kø. + Forespørsel for å finne krypteringsnøkkel er satt i kø. Queued. - Satt i kø. + 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ø. + Trenger å utføre arbeidsoppgave for sende beskjed. Denne er satt i kø. Acknowledgement of the message received %1 - Bekreftelse på beskjeden mottatt %1 + Bekreftelse på beskjeden mottatt %1 Broadcast queued. - Kringkasting satt i kø. + Kringkasting satt i kø. Broadcast on %1 - Kringkasting på %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 + Problem: Det nødvendige arbeidet som kreves utført av mottaker er mer krevende enn det som er satt som akseptabelt. %1 @@ -376,35 +376,35 @@ p, li { white-space: pre-wrap; } Message sent. Waiting for acknowledgement. Sent at %1 - Beskjed sendt. Venter på bekreftelse. Sendt %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. + 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 + 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 endringer lagres.) + 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 endringer lagres.) 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 + 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 endringer lagres.) +Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne filen nå? (Pass på å lukke Bitmessage før endringer lagres.) @@ -469,12 +469,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil Are you sure you want to delete all trashed messages? - Er du sikker på at du vil slette alle kastede beskjeder? + 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. + Du må skrive inn passordfrasen din. Hvis du ikke har en kan du ikke bruke dette skjemaet. @@ -489,7 +489,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil bad passphrase - Dårlig passordfrase + Dårlig passordfrase @@ -499,12 +499,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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. + 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. + Bitmessage vil bruke proxy fra nå av, ta en omstart hvis du vil lukke alle eksisterende tilkoblinger. @@ -524,7 +524,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil The passphrase you entered twice doesn't match. Try again. - Passordfrasene er ikke like. Vennligst prøv igjen. + Passordfrasene er ikke like. Vennligst prøv igjen. @@ -534,7 +534,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil You really do need a passphrase. - Du trenger sårt en passordfrase. + Du trenger sårt en passordfrase. @@ -559,12 +559,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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. + 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. + Ny oppføring lagt til i adresseboka. Du kan forandre etiketten til det du måtte ønske. @@ -574,7 +574,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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. + 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. @@ -589,7 +589,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil The address should start with ''BM-'' - Adressen bør starte med ''BM-'' + Adressen bør starte med ''BM-'' @@ -599,7 +599,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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. + Typenummeret for denne adressen er høyere enn det programvaren støtter. Vennligst oppgrader Bitmessage. @@ -624,7 +624,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil You are using TCP port %1. (This can be changed in the settings). - Du benytter TCP-port %1. (Dette kan endres på i innstillingene). + Du benytter TCP-port %1. (Dette kan endres på i innstillingene). @@ -644,7 +644,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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. + 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. @@ -664,7 +664,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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. + Feil: Du må oppgi en avsenderadresse. Hvis du ikke har en gå til 'Dine identiteter'-fanen. @@ -674,7 +674,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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. + 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. @@ -684,22 +684,22 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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. + Angående adressen %1, Bitmessage forstår ikke adressetypenumre for %2. Oppdater Bitmessage til siste versjon. Stream number - Strømnummer + 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. + 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. + 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. @@ -709,12 +709,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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'. + 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 abonnere på samme adresse flere ganger. + Feil: Du kan ikke abonnere på samme adresse flere ganger. @@ -724,7 +724,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil 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? + En av dine gamle adresser er av den første typen og derfor ikke lenger støttet: %1. Derfor kan den vel slettes? @@ -754,76 +754,76 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denne fil Done generating address. Doing work necessary to broadcast it... - Ferdig med å generere adresse. Utfører nødvendig arbeidsoppgave for å kringkaste den ... + Ferdig med å generere adresse. Utfører nødvendig arbeidsoppgave for å kringkaste den ... Done generating address - Ferdig med å generere adresse + Ferdig med å generere adresse Message sent. Waiting on acknowledgement. Sent on %1 - Beskjed sendt, venter på bekreftelse. Sendt %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. + 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 ... + Utfører nødvendig arbeidsoppgave for å kringkaste ... Broadcast sent on %1 - Kringkastet på %1 + Kringkastet på %1 Looking up the receiver's public key - Gjør oppslag for å finne mottakers offentlige nøkkel + 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.) + 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. + 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. + 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ø. + Arbeidsoppgave er satt i kø. Work is queued. %1 - Arbeidsoppgave er satt i kø. %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. + 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 + Problem: Mottakerens nøkkel kunne ikke brukes til å kryptere beskjeden. %1 @@ -838,7 +838,7 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. Subscribe to this address - Abonner på denne adressen + Abonner på denne adressen @@ -848,7 +848,7 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. Chan name needed - Kanalnavn nødvendig + Kanalnavn nødvendig @@ -863,7 +863,7 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. 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. + Kunne ikke legge til kanal siden den ser ut til å allerede være lagret som en av dine identiteter. @@ -873,7 +873,7 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. 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' + 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' @@ -883,7 +883,7 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. 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 + Selv om Bitmessage-adressen kanskje er gyldig så er tilhørende typenummer for nytt til å håndteres. Kanskje du trenger å oppgradere Bitmessage @@ -908,7 +908,7 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. Successfully joined chan. - Deltar nå i kanal + Deltar nå i kanal @@ -928,7 +928,7 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. 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) + Bitmessage vil bruke proxy fra nå av. Hvis du vil kan du omstart av programmet for å lukke eksisterende tilkoblinger (hvis det finnes noen) @@ -958,7 +958,7 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. Search - Søk + Søk @@ -978,7 +978,7 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. Stream # - Strøm # + Strøm # @@ -1126,28 +1126,28 @@ p, li { white-space: pre-wrap; } 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: + 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;">Pros:<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;">Cons:<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> + <html><head/><body><p><span style=" font-weight:600;">Pros:<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;">Cons:<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 genererer tilfeldige tall + Opprett en adresse ved å bruke generatoren som genererer tilfeldige tall Use a passphrase to make addresses - Bruk en passordfrase for å opprette adresser + 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 + Bruk ekstra tid på å få adressen(e) en eller to tegn kortere @@ -1162,7 +1162,7 @@ The 'Random Number' option is selected by default but deterministic ad In addition to your passphrase, you must remember these numbers: - I tillegg til passordfrasen må du huske disse numrene: + I tillegg til passordfrasen må du huske disse numrene: @@ -1172,12 +1172,12 @@ The 'Random Number' option is selected by default but deterministic ad Number of addresses to make based on your passphrase: - Antall adresser som skal opprettes basert på din passordfrase: + Antall adresser som skal opprettes basert på din passordfrase: Stream number: 1 - Strømnummer: 1 + Strømnummer: 1 @@ -1197,22 +1197,22 @@ The 'Random Number' option is selected by default but deterministic ad Use the most available stream - Bruk den mest tilgjengelige strømmen + 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) + (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 + Bruk samme strøm som en eksisterende adresse (saves you some bandwidth and processing power) - (sparer deg for litt båndbredde og prosesseringskraft) + (sparer deg for litt båndbredde og prosesseringskraft) @@ -1225,7 +1225,7 @@ The 'Random Number' option is selected by default but deterministic ad Add new entry - Legg til ny oppføring + Legg til ny oppføring @@ -1248,27 +1248,27 @@ The 'Random Number' option is selected by default but deterministic ad Special Address Behavior - Spesiell adresseoppførsel + Spesiell adresseoppførsel Behave as a normal address - Oppførsel som vanlig adresse + Oppførsel som vanlig adresse Behave as a pseudo-mailing-list address - Oppførsel som adresse på pseudo-epostliste + 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). + 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: + Navnet på pseudo-epostlista: @@ -1319,17 +1319,17 @@ The 'Random Number' option is selected by default but deterministic ad Bitmessage won't connect to anyone until you let it. - Bitmessage kobler ikke til noen før du lar den + Bitmessage kobler ikke til noen før du lar den Connect now - Koble til nå + Koble til nå Let me configure special network settings first - La meg konfigurere spesielle nettverksinnstillinger først + La meg konfigurere spesielle nettverksinnstillinger først @@ -1347,7 +1347,7 @@ The 'Random Number' option is selected by default but deterministic ad 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 Wiki: + Bitmessage er et samarbeidsprosjekt, hjelp kan bli funnet på nettet i Bitmessage Wiki: @@ -1365,7 +1365,7 @@ The 'Random Number' option is selected by default but deterministic ad 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 en likesinnet, 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. + Du har opprettet minst en utgående tilkobling til en likesinnet, 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. @@ -1403,7 +1403,7 @@ The 'Random Number' option is selected by default but deterministic ad <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> + <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> @@ -1413,7 +1413,7 @@ The 'Random Number' option is selected by default but deterministic ad <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> + <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> @@ -1441,7 +1441,7 @@ The 'Random Number' option is selected by default but deterministic ad Number of addresses to make based on your passphrase: - Antall adresser som skal opprettes basert på din passordfrase: + Antall adresser som skal opprettes basert på din passordfrase: @@ -1456,7 +1456,7 @@ The 'Random Number' option is selected by default but deterministic ad Stream number: - Strømnummer: + Strømnummer: @@ -1466,17 +1466,17 @@ The 'Random Number' option is selected by default but deterministic ad 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 + 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. + 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. + 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. @@ -1494,7 +1494,7 @@ The 'Random Number' option is selected by default but deterministic ad Start Bitmessage on user login - Start Bitmessage ved brukerpålogging + Start Bitmessage ved brukerpålogging @@ -1509,17 +1509,17 @@ The 'Random Number' option is selected by default but deterministic ad Show notification when message received - Vis varsel når beskjed mottas + Vis varsel når beskjed mottas Run in Portable Mode - Kjør i flyttbar modus + 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. + 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. @@ -1534,7 +1534,7 @@ The 'Random Number' option is selected by default but deterministic ad Listen for connections on port: - Lytt etter tilkoblinger på port: + Lytt etter tilkoblinger på port: @@ -1594,7 +1594,7 @@ The 'Random Number' option is selected by default but deterministic ad 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. + 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. @@ -1609,12 +1609,12 @@ The 'Random Number' option is selected by default but deterministic ad 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. + '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. + 'Total vanskelighet' påvirker den absolutte mengden av arbeid som avsender må fullføre. Dobling av denne verdien dobler også arbeidsmengden. @@ -1624,7 +1624,7 @@ The 'Random Number' option is selected by default but deterministic ad 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. + 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. @@ -1644,22 +1644,22 @@ The 'Random Number' option is selected by default but deterministic ad Willingly include unencrypted destination address when sending to a mobile device - Inkluder med vilje ukrypterte destinasjonadresser når mobil enhet er mottaker + 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'):) + 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 + 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 annen 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> + <html><head/><body><p>Bitmessage kan benytte et annen 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> @@ -1699,102 +1699,102 @@ The 'Random Number' option is selected by default but deterministic ad 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 - norsk + 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å utover 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 - + Resendinger utgår From c791b85fc96026cfca967a63492d7d5892d5fb27 Mon Sep 17 00:00:00 2001 From: tranhuucuong91 Date: Tue, 7 Jan 2014 02:33:36 +0700 Subject: [PATCH 26/55] Add icon scalable --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index e0375964..c234dc29 100644 --- a/Makefile +++ b/Makefile @@ -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} From 075541347d836d12df9953b740a62b57e7c9f817 Mon Sep 17 00:00:00 2001 From: Abdulla Almehrezi Date: Tue, 7 Jan 2014 00:28:42 +0400 Subject: [PATCH 27/55] Arabic language translation --- src/bitmessageqt/__init__.py | 2 +- src/bitmessageqt/settings.py | 4 +- src/translations/bitmessage_ar.pro | 35 + src/translations/bitmessage_ar.qm | Bin 0 -> 52231 bytes src/translations/bitmessage_ar.ts | 1680 ++++++++++++++++++++++++++++ 5 files changed, 1719 insertions(+), 2 deletions(-) create mode 100644 src/translations/bitmessage_ar.pro create mode 100644 src/translations/bitmessage_ar.qm create mode 100644 src/translations/bitmessage_ar.ts diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e79af31d..2ccee9f9 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3320,7 +3320,7 @@ class settingsDialog(QtGui.QDialog): shared.safeConfigGetBoolean('bitmessagesettings', 'useidenticons')) global languages - languages = ['system','en','eo','fr','de','es','ru','no','en_pirate','other'] + languages = ['system','en','eo','fr','de','es','ru','no','en_pirate','ar','other'] user_countrycode = str(shared.config.get('bitmessagesettings', 'userlocale')) if user_countrycode in languages: curr_index = languages.index(user_countrycode) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 7c7cb61a..4d5af74a 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -89,6 +89,7 @@ class Ui_settingsDialog(object): 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("")) @@ -400,7 +401,8 @@ class Ui_settingsDialog(object): self.languageComboBox.setItemText(6, _translate("settingsDialog", "русский язык", "ru")) self.languageComboBox.setItemText(7, _translate("settingsDialog", "norsk", "no")) self.languageComboBox.setItemText(8, _translate("settingsDialog", "Pirate English", "en_pirate")) - self.languageComboBox.setItemText(9, _translate("settingsDialog", "Other (set in keys.dat)", "other")) + self.languageComboBox.setItemText(9, _translate("settingsDialog", "العربية", "ar")) + self.languageComboBox.setItemText(10, _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/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 0000000000000000000000000000000000000000..477a758648f4438322b521954e9cb83112704535 GIT binary patch literal 52231 zcmd^o3!I!~dGEW~-Pz4%vk5VBGr~v0W;e^;awkg&>~1!fg@ix?0jj|4&SZDU?#wzf z%VwbW|FMH{W}E-sgSp&+~5oRPOYz{{8)L``S6HzHq~PKKFwmr54|*lv=OUJzrF+ z;clfm-=kFHqe@+Qrc!&lm1??5snOReb;n1QI`kE#HvfrIzt)A{2bG%KsnnP5QVri| zS8Clus^PmYRjTWgs^KU7N}cx({rQ@YsK(bfD0NXzHQtZ+hdznVLrU%Z4b}MJ2bKC{ zlWP1r`n~$Is`2}uQR-EHtTG>{N579@=niyJ`jt{0uU6-s`Gisj zeobw-`vIk{JzH)4&K*h}`&+g9-Y+Y)Na@d)Kc)7)?Pr+J57fSUTQHv{wf|nM@x1lw zvKwz!>H{xU2lh@X)$?U_#j(#S)%yeedGkK~dCNKKQ2t@eXSurSbvG*Yv0in!fOa2y zSY7>z9ZD@dtgimbHl;2eS1-xDPN`29)JuOcj&-%Gqfg!j7(J|J{srT{^{ASEZ?974 z>{quu^m3(Ia_ZNM_bK(E_p5gtJ4>lM8`W38aGg>wenNe7+3S_s^lJ6J-+csdzg_*{ zMHu&-K*K@2|Y6?uKjLtJH1R)D^cqfO&qo?zJz)ye7}9yZePt zD0S%@>fZGctnbsO)P3f`dbIz`x+ibnq129t>i%wiqf*O$Rrkv$fhw2$b=|Xzh5_f_ zUX*zo=0AGgqUa0w{;FFS^*o9F+;ab-={t*f|ILeT{Tr<7?DsEv!^~4kUDv$mp_kzK zx>buFDm|jq>8C9E+70-;_$!P4^w!^1>d`+~^u4yMQX@ZG^j9DL1n{lA=*JBerEbYB z`sux=DV2G&zV-EunBPz8mwxl(`2KADvR8i#zkj_xx*zj=$=mAtzx6SUyT5+pYccMb z2kW<#Zd2<1Z`WVAKT_&5FRvfG{<}(z7waFm?$yBKXX`)R3VeC=uKKUO9pC%!tbd{@ zuT<-H{rQT!>%aTdJvbBZtN**hnD?#&^-mo`f7vhA|I?=~S88CqVcGTA_eWMWoch6Q zl)7rB;lkhEsnmxbZy4VH0qn~Q8uok*`_=hB8uE|!0-t}_aC8aQd;8BC=KclnDxTJG z?7=6npFe7No%%?_hr8d8&%bDR^p3;W|KWzm?q7-X@wW|M`pG(_ zK5|;am*?A+I_-xI-&yfE#$Dc6_kHw#%8MJ@IyNgc{E5b`KYNE#8zvgJeHHlshKm{p zChL{jn{6C=dcRUDKGb;0livnD>}tI3Ho$pgZ)5H-;C#_{8cPp6hJITbtG9G1_0i>x zciawmJa%E@JGNom8?SD>@8@{$ogZj?*PZBp=&r^O-i3YJ{nN$|-E}Ky#aWGyytYxP z<$uul=?Tnd_uCtvxC{9EkykW+#!YrOv-H z^Ux>1hxxxf^TFuzIA=f0Jod&HC^fe`^V{6V+drK7_6Jwsy#6TjgR3x~N4Gbvcr(u5 zmOpE{U^n(@`E5=6zWW5$(b#nPhG)0sDA%(=BJW;XM9c(`zn0 z1Lx{{O?Umzvy{5~j;6amjpxlPn(jOLh*Irao8CMBD&Spn(}!OFm%x`U{W<>){dx0` zn;zf%fKs1&OVg8of%fltf74&@C;b1Z=@;#v$N9Ur`TWd3V113vBW*7N9^Bo0aL1KO zef*;4Yd(Ju`g^wdmi&Iu={uTVv+q|*U6X5m-TSTu{uG<5TNc0QxBe0Hcxdt3Kg9g{Kf8GAOU6MjZ(Cg1 zkN(zueQ{+5{p?u1_`W{@-tFyKeE+KJK!+ABe)slgm3qZhi@*5cr?5{CF8&+PHnp#D z@za~vV7)(C{Os?%QK{|MwyeDaaCqgPw&Z_uDbCTaS|)Q3fFBIB-1sE=tA0~|zT?kZ ze*OF%O1<>qmN#zsyi&jU;+98t{1W?`Yx$iePXq5BZ~2}3e~$70rR8%4ytn;pEx&vF zWBC5LmY=@;Hl==RY)Rd|M?l}MSh9E?cyZdtm$cM>Q>hECT(b6Q^p}}f()qdTK#v|= za?uO0&xh|_vVRoky4t8e-_f+>@@E0(N8Yn!{1))MD~~R@>*hN!?j=j!`~~3Oo@bWa z_pM=^i|!@&FWv-rzjev)-wyn_zOdx?e~57}`}UGQedm6q-u3n+-<^L-sZUHT`QDzl zVcav8Joz5r>&h=K`N@h7z~L=ReiGrlJoM()a~{1?shgWydozCkx_V)2|5cdZuBTfs z?gm_DZ*SduZ&9fWuW7yd)}z?ZkGAIi@p{m$`PTfs58#|W(|YtHLs<9f*1LZ1&ERW` zTJO2&Owhk=t@n(LDpekB{lKwL1K;~wKTdo&*x&le&F{v#zSR1u683lQ8?B$ItKeLn zu0J39OzY=XV*YRaNb5Il#OJRUTEDpr>)3Kp>$lEB``g#Ge*czlfiAzZ^%sXQepG7x z#kcVL>h~>e{_!V(pKF)4?ZWr7Pha|?slQZe`eRGiz7zfT{qLncZ~dTBxBk)6i~pew z>-qZ9>mPn6*8fLKbNjvo{&x1#qtD!?)Wp)Ivrpjjyss?1dH7A(zueOAe6<(!{@Y7` z^rkVu|CLMs&*a6xw{NzoFJhdxUE9{&upYmk+15qA*87^a-u>^wIl8WG&oi&XK8&;- zP}sM(p4Rr-UxGj0@%^?p{XO10w6pEqzji+8dR^Ne-4A;Fnm=!Qq7V2`f35zUf2IDs z`R=yw?tdlr;fMP3zO`*n=B`s})vK3nD9(bfU$<=Y@1F(v;^4B&9^9eSkrm6X{pjbF zdhHp@9@*Un{`>i5pE~;i$R(}I9)BU`d&T&&U%mnKgQ~$83x4*A%WdG`D^<=d= zGtk#JH#gTix3RZWp6EMtMc)-8!#$JL>8VZqeU)mtFkbD=RdcI%v~1_Y9or}K+1!rp z)A?#PDrTqi7p{I8-kGfCk5yMk{ zOm{R@h;E7ICJWVkPh}=Mo*#&2%K4tTa(3o|=+>6)7#J;gZ12;9jg@lqkvzI^b#w${ z^c=}f7pCS1I`$om4o>E$rlOrw6VZl_3-qs*!p-@CZ8KH#buM3+n5+(L>hHI|v-g2w zsXU#Xx_~R2vAy@atlBe^EoUb%lgS>=d>}tPQ=Pvcn$DId3dNpkX=b2*=9v3FR;pG@ z)3HZW`6JcXgR%@St=RwARB8NZPobCtqy{i0LdJbX*Q?aFjzMpTsP$ZW)4JMqbH?U_wz-;r4_ zzwl}J>rTA49#1;(xlTo@U9EQ`bq$UmEtck{^0|rpG*%LojzrbTd^DY}RI(HKsGJ|q z7jDYuqW1Mk(>V)6Z$Za{>^Re%*)5;#nA&Cz=Z0A}n9D``^K%hC%ji0(uXE8?U#6Gs z%#yk=?T*Z_^xT&j#jopJk1N?@5j`H99jlC&3p3S1shHGlSK<^q>=Y9_UuruCM9T9? zy>35YuRAf_L5yPe1pAA{?7=epKA+8TDtX{gIjWYT`O<9JLMR$4m5wHj+(rNqThLcW z&{xxOKzrs%%%#<|PaSw|d|&`>1XJk7e|`9SBPKP7H!g8IbeiojJ5>g;nI~<)S`*Pa z0-%V&7{F=W{x2SFfh?zbEXG>^*9LeRPZ!J zh2l-wsY2pXEM(fT;a!+|H~LA2jPI=yO_YlHq`93QZ<{cuQ{0>?kxZ&Lsh`yg`soIW z;e?_`{uSbZn^(0|;)zY}Z+&LSkh0CvK0G#YL_gKe@TPpZfl5AWqB@J#NYf-s=Jh>&uL|Oy8Rl zpkZvEqe2C=QZNZ(LK(m$>BND|O##FQ^5x0wOogNrxCXw3-)3hZ2jud>aESn)dM*GL zK!hNFP6Q?Z$yEVJ!YM`;B3ucmX1UiYSW-@|NBHkAu~ae^M-)PTbd(YDg7SRZXkBs^I@wHKFoub}jytB?0ty zZkjBdq-!f#Vp4Z_gWJ0;a~>EdX)}-h-dZqGv{d9!o;KV71DTBs(WOI@oF?jg(>YfM=e$*d*YAXsLlBw%}hBq03e z2_;jaNT(o#J}_L%YB@TCBJpn1ybo`Mw85a8v}*_CI`9?`Oc&6OvH^}(%G@aPQe*@_ z#rr#f{GR!6Y|gO*Ga{<k*7$Ijfoa=G0x_p#BR;X0T@t{!_ z^WzlJD^X|RNR%zkuS>+`vt$87_;)Q3$5NGzSOkTNa~xJL9-7LIADu$?Nl3j?deJmN z=vYkWSvF)(C>NNl*zCaO5EZDB@Z=Mz3E-elP?(O`EFq1f)%kep^{Z3-STCE+&*pQ9 ztEP@(G&GE}6B{;yO{a9qF-fPn$=mSE>keCe&wzIxOwv&iaL9_*ipFp>@T({mhTbH> zYZb=WijnqYI%UmTBCeH&mk(R9S6FMZ=uOG67ZcVrf@cc?wOa`B)dS}R!;>YVNj93v zRw^@-U;v3y>(f{~0kI3fWxwtCd#~x!k!fJOp*|^RX#t%Bs{pe?KMTaA#J)Gw2tT9P zCW0$j2nq$DwA0P=Y@}UbXI$eNK#$sj7n*JvE)~b~XW_lMMxY!{lTN}jT_vW87hD$T^4yM9z3NvG+Y&lVh9&)1u{j&{f-ehev(6}gr zf=&ppDA1}|s21{-L=w=dh9Pg3)s#9gUM*=&%e4;sR6RPyp${W|Kve(?;4+B$LzRe0rQ1i!gst0BGkIzX)CaugvS3O0t%e+;4W@1-D=c9c1c$xQ)Wk*Y z1o_;PS&P4j0zH}Y@fVF~$J1(JWKf76jZ19bEwZ8!g&|z;t^r)2G5JxTlT!4io@R{} z)abM@yBbi?1al95-(przvTomKm(xTce+&BXMDzz{LoU;FF%Z45AwVAZy3@OSLjvRr-h+USZXJ%nNieM@u{uKE0Rr?ar{lh@q zZJ8}*FFUb^ge#bfd1A~fvPTw@+_iwI3Z{@!ku7Pnkg*3VGI`JXwCTp<({34wz8-z# z>|Vy?XwbR!=wmp>>~1srbg(p?k8)W!G+}KikqTg~!LDIHtnF~BgaaQPngoafGE&6V-Hw$!kFrF5h!<{ir&v*RzDh5HU-9jv!W#QWb4kV8-qNH?fL{d)uQ<w0o|N**q-D}4hf1Hcf?7oY)|sauqTvS>9Ws}WQ~_* z3ixZhRE8(IIyLV|;BlAtNU*$`jS?&g{504o`o}~23mBwhN))*??_Q|v8);77)r^On zpM;Nme|1bYUyX5w@T-<_0&`<*yYEZ@iWuYKyawc7i~%L#IwWe=8QL3NQ=097MQ<{D z6U@=D3yFY9p&xvK#H<4^l%u^)zVC>t*)cd=z?|~11G3)~ocU}3)RVKAXd4hWfak%G zF9Bye_^=k_C`)t!G-9CzfI>%7jP|Ekbu^A@ZsNH^1d=f9MqLw}sNYBcu~o^zFFcQ! zgeM9zBUmPwC%JsBI|abf^8Z`K)hvbcb><4>NIU&36!#;3n-|dSp_jg zj$&C!+mhEYh~rUpt^n>C9ik`$ES(mN27)@9G4)ZHiV1oeo)aN<-}8o;gN8uZ+ZCO0 z_jyBU;yFTzMr{in>&_Gi(ZBQ@;q&h24Ihh9#59H(bpd-2lY{>#6*}lWZzz?XBb3Cz zt)qS*jcZ(bzR-FgfR?rHYHKznVNK19-wJC_WE+OMX$^_`JEc|qjVdnBG4(KQyrDX~ zTQqtL<>N6Bf9|diU^P7pY^luT#|uZG{6@Rzx-sXhH`=R@526p?CKi@}L)DGw5NqyD z&TI^=4&Epr8;By~Qq=9 zSet`BsNS1I&ov|x^yEXcHp&EYq9zu`XQ!(35xl77G8W$(9fT$uL9nL-8^iSPK{9X~YhIhSm3v>x0;g_;bTK?}|WNn+FCq9U9%1Cl3$fQ<13 zK|er35w;i}1wCe;4nc*IMi;fVSyQDKxodB6taJ>a6WYKBgN*3%7Kza_{fb89b*^HH zx`}wa3dc3>=cS32R&^+J4pEluot}ZZOLJ)=uAC`APTU69N25$U2|0HG5r}1WBN#Bj zXsXcAG6Dv*!I|IqrZIg1kzk|5|NGrYXYVcE1OreB^3@dNtyu&`%|MdX#-f3!KWPr6 z-@9Z)34~DeFmwSisnd{&)p~?ijFM{iMZ<75B8rFMM@ik18fuqvV9s?DZM-xKb8J$( zp`>=0hKa;bw0~Qjnv%#`k@(4%-T85&yhuigI;mZPxWTFhtV9?B7~;C5vAePG9&Cwb zH$uO#IVTF}J@rz={^-xI`5EZLVWAB8QF5k*O}vVJ@hE$+bAWKLsJ8-hx^Dm>TmBug6sg^$zzxc))^)%M5evHvm`E4(wDm83>I_aUKi5`ymf37tCvr_6g=Hnq6jzwr z_@(c3ytoc)&EqdT zREz}U-*C1Y3EsM<$aS^FoY+Q}55)(%aJ>Zo3PP1pYx!yMCCE-&JF3{gYJ!Bh12+|r z?jYPxUhkIJELe8%KAh3ukw8cof{t3PnVc1VYEXR=XLK-;$ID=}z}iGMczim&lwvw< za;fzCHmsNSPaQ5riNt#oY+!ZV)E0xgoMB5!;_TSAUg+HnPQ1g}{{1HMHwQ0G3G@zY zr+>zZ9=*|FJ_X#48S#DJcC3M(PDYP zWSQ9G{ntzPgPWQNkK_oGAK?I5byqV{Zh0Vxa?{BdWb=7+(SLB<5)6&z-I{*T1_w|M zo^8W7T!0luA=&Vpu@AF=(NV0owIpM=%z^BLM|dkf8F`@vd!gOZiv1(_Gl&|v`LZB} z!a>)00wENapj26N<9fRRz0p-B#N9oDM9bq_EJ1iHPEc-}ouTlFEsFLr6q83=LZ@+{ z5p$UsqOe-z6-r5>wgLo45h6T~5aC{xgGycvrrvhhOkN@ZD!uTbC3g2ogIx62heHBqFlb=p74qkyPi2BAs~H5(YG3W|3j*sQBUTZhUHys@V1q9LB+USc7pxp$T$5dgQaT+9k zu@~79UzrDCmHrCTGgFdB2jUKkx~A48p}>?FD)SXk{KP}n51!x%IN*XO;moW^hEfuZcqKr%2_%eQXFx+3MG{7k5UnXqgTu-u+OQ~lbg0K_y)tH_ z#=Et>c$x;^Z%p{R{6M(`fKCt42@Cu0cmZynVzr}UBjs&aispqAr4sPo2Q`^+dQZ=j zrN-CQte7jaoJ6nE2fb?I`1Y~{j0a(M()(_ak@m=<0K4sxhz`ekx(=Zl?&Dzj+7LLI0Wi`g7`6s6u2 z(U~S&P9+v%!70fzE=F&YHZ2e=VQxc&=`t1pvx947p%R~n%ap-SW=J6bqQ%+t3oDh% zcZcB}u}R=GRZ2|Z?a_Z(EJzMjvw_^{%h#CT)O`R%XRj z{^*)FUtuz&lSuBRSbLZWNF(Nnod*Ub=5te)jylAS zKCO+I)Ibn)0-;We+#QLizXFJWFlWrR6LS}fnS*-EK?*H{m6UAJGhI`HEnMud>tcm; zVP}+0>nelMVS04UG6xYXJf5WbBL>!i*uCDsbtmCQFK~ksJIUQ}9j-baAd?2_Kkh)g z0bar=$#rmM;!qQ5{)Aq?d~~|gkzS42;xfSVHS!#aja$(g83rg3;+6K{;SkT{OAsQH z7DmZS6JSjo%{F5U%Qu}%EaSKRG>&G(1hF0%984M^$%JiWK{tDDa3aBE@CJoM#H$$? zZwB@90xf0u4fxgjQI;Nr;GDe)F?;0{yl#=b+5(i^>L$>PRy4pRQaQqcz7eq!CB=-m zZk8Q{88`^wNP@??nDVZS=G#m_^YIXlE(WHzQx6sp5vZMJvoo}2W-GZPiH5k|ljzux zyqNg-g7^@y_#}(IR|8_+_(I)^h7wO$sS3d0CK_1iVQ>h99ZU@3bl~ebVZ0^C9TT-B z*fI&YSN$8zF(E$E&9L`mXQ-nXlcu)0hbNII&( zfNBz{Fu;OHnnVe2gW0Qj)`P*TQX&%rxd@*?Asla43G9gmjQ*y!Xj{K|cBzG4}JN!MJxv6mZ6r~imCtfLrf zw@9#BVH<@&qt8O8{IUoAaU89yu=m7p067EHkGwC+uP4-iy!K-}3DP^alols?#Ge{s8~aBnka{|-W(EJ%1Abb<6rz=I`BhFzAyqVJb`F{^Ckf!ILZ=V0y9}@qKF!C z`Y>zdEL%ZAIj5Tybd<<7rUk}_eNn4#5-+0TooRFHz_dH)P^*?eA}}|J3KOa!ozONX z1>kx_BjhsZ{d~2 ztE}nsk`m!>a3hTG=Ym{}Kj^v6K=foLQsZh8K8A648Y*fQL4Q08n{Z-0eHacGV`xHZ zynocm-H~Ab%ItJ>B#VH%Typ6MIs|ILt%LU02!CS`oKMSJ4Z4QJJQeEvpj;lzu?RTH zyzA&d?p;Sm5{wTm7@KqH7n-pKqxRw(oELzGr<;63CkFcBQe7ek?8Yx)7RdT=UPON& zv8xAUYKI}%a{{FhiI^$z`&H02o_4R0QL}@BhFNhg2zJ6>!E7atjqgabcrbTLQgn)m zA3|sur2^|mr{A2^la$c!`T*D9%RG=FyJblDE=<@#IU^kyo}ECJOXAsjBc`RL)xg7- zCU%I>qC>kiL8NDj;dZc63PBCU5fW^Ye@fg8QP88r6B7uo;Jsj(6I`Zs@M&RLmsAWy z2mHz80yy==nlCbkOCHdXbK>iBxh@OKH6+5M>ee5yZ>+=#7P6k0(?&!(ZPql*M38hC ztL|{MO$-Y&uo@B8BVjc$B*n4D!njNkN-VjOj1-$4VDr!U(o7{kn=AE9<3tl_8TFpH zstlMt_Rh&Jiw>2NPGGZsEl?rq50w(N2igT0HA=t`BdC}f+5^>e)mD1n$$Cl6~`Mu+XFU$BaXk4D}%AM7ZE5@ zK~yoo4#X?-ggDiW7ID!jbvACjms}~;V|;*TCyMRCsKat%FvXN4sl*sO;aWU4o}Xa` zcv5U=Vi4fTL5bECif<4=g&|Tj+$Ax2|1Y5AW>X8L!>rRQ$t9gbtX=yYpyZk+LWx#j zV{&t2He7{Zr6k1L?q9~{dpCaIJ63ztA@Jr)1-W)fq!j_WF^JiY*u5@;%v4|4*(hMb z_01A%ti+o%^bNxP!T`b0YekH8lMqVj*|HQ~5Xwk)NG#Qg^IrUw6koqR2obDK<_qN- zw>AQh7_Vh(E+B+MI2}aXltB9*VrmRLp=p)Adq_0mdG@410(0<7O(;u>_Qs|8oI5T( z^o!H)0(~C6QPc8PX1*8d0n{*TWZh09W5s(7TT}EUz&KNQ81NV62*%IRL*yleI}c~2 z*wFxU8O6^%K8x(nBCB9CkuZUtAVK1Y_X4yWuwuhC-?1r7x zh&60&Ma{S=JZ&Z9R+hgYk=M4fP@-!+Tn0gjkTwECXVQ7AIy5EUFm+MU83RSP$|<5i zDgr1D{IEKpXKAQts2)4Nf}BCET-QQad;|d+4O;BcO5UO21Cd1U(~<*y3W5qnE!4fC z31tFg14bNTNX0l8l9h~efv(*DE-lpKFY-}7;o+!_kAE@ekSpXEBP24r6^^I^C&KKH zaWF%(02O>qaY(CZ1fvc~LmVMTGk}Ibsoi8C56#sQ-wds|s=$WP4gD&a~{6fL8=sAsSBV5z^W;kDeLUidwJe0ej1qNdT_9@&7*n zV~chhn-q%$cj9{|wierlzlfFwA>?lfSd>kTmwv6SEE%ddY4z)Dv{Zt978S&Fu76)RYh@lge-Xs~Q zPCW;-3c+>qXO$*lpY%=b6g8I22K#tNfnc^bBk=SKwI@n3znfH(iSNybnAlVQ<+I7LzS z;%vE!aYC>u?CaPHYy0w&6wkevraILOGOf#~(MMgyrn1wVKWZ#KJ}S)zGLUUJ+ZUv; zgsRfPIo~Ba#O(8gD!`0=CMOq(%b3BmoF)F$ENF5b6{>J4O_JdFu$yvdt4^_A3W&%a++358DMl8#THs;2_T57_=^CowR}?xrO2gIu%Y=lb@#9X z1})iSJmtXHnUaW42$3ggvze5=0m!Jw+Su_#=yW@=nsHjpmf|Ms?f1f>w(NQyEXowT zr1X|TO_E|JVcp%tq0YhplGG)^bhMzdjt%8d2pRz{oPp(+qmDzR4hUX(a9%7)F5F4% zm1)>}0BCXPIuI_CKK8o8G)ag-&Y5PywyLlWPxoOJZDYvD^oHl8ZjWIqOj{U)8HXk< zn6B`z_8No&ZD>}-67Y_0rww6R0Jc$IaU&PiAFmm21|CU9m|OggsB?_f7g6uT;scM* zkphax(tZqy*;PA*D-8Ihv!b=%TO^X@ND+b)i!P#_h(#$)$h|_O7&f)epZ_p6Xcy4j z+-6}G#EBB6xVRu}+D*2vvw~PeJqs31gXCSz%@uMeY|r>dFllPu6q{yBbNO=G?7~~G z7Tp_MONBd=^mR$NI0@HE(VB^ip6s1b<{GFixCUzV4#X8#25`?3uUX=&JAg#iIVd3k z`Y0OL8NH5h+EJFXRq@2lH;p3lWt5hZj@ScFM-LAq;`3oZ$nrKO(J#0ts2G5MxkgKY zh{8Z8TB5i+)Gbk)KF^y;^y?l4%|Itw8;s5i*CypYT*sXtvvZ<_;~uHvy4rzkB4GX) z;*((bEK%K+L>BxB*(!jLs9}PA$bVtx5exkW5Q&dV+_dh<8fOPFfIr6yP{Y*dD(VUB z768LkDjBy0!RPYXqrs^a#3HUq#H1GF_tFVgM4X4{@nsY_>;dgW{9U2i%_(4)WfO4x zWlU!W)|-D)!nR}Aaj#05Ln1)tjOBBT$Im7$xswZRXi7b3}vcuSKuyEqdXf#<{cg}pG% zl{bgbU*UqYj&g`M3#k0dVo9}zk& zFu0K~T9>RvG2Pnb0-!(7e%b+B!e5s!)j{yqC~HUckm8dd+W_D6Ov$#WP5Pbg!Q2UB zuGF$RnvK`WGna2(H|vUi(6r+c6F>)Rn3kc8RLCI^A>k>X>(+r=TEj*~9x{@Wu4<|| z87MYI3Al1*aho4azSn@b#s_k5!iuDAeI|de%-(_;_(vEFQF zSi0mXR#+|$K1i@txJ^N|V4kjsV>6j#he*YRL*hV)-wXAFF46Icg;p?T8-J$U z?yHOUf}tX;M+%{XtJF~UZa#<>Fb@D)D+R|}2$!Hut8@=i^pk@TOOW9*Y}JqWMLu+0 zqE4BkzH8I#e^|#LrkW;$p=@C zqmlQSSky)Gix%+=n~;nJTaAhGo0y73*I{z3-^k4xT`6pOX$cz&0~iH7CICWqs@dM- zpyBD5pC^yOJRFc(4!FG4bdd0dDsm;}Oj)3K8tNz_LEt(*LmxmWI-VM7B9T50l_~%q zqnw1w=|(!`ioEE1UYtoE;Vl5YP9)HA`F+Bl)=JKEk|li)ez%G!X)(eRFgw}=Ow4-% zL^Nl!aPm~2C1`o4mHu76k#o9rT#o>DP!>yS;5LUeC0A3xm0CtXhltB0)71jxeTx>9 z^9-_d<}2%>^$eWbKtri1M$1Y&$XS=W`I4#CnZl>7|Abae2CY+Y^4G*HkHwx3L=u0I z+#OlIW$K#sC*q(0G89FE1MPsaS_c>Sy>;t$2PA#B_t#8>No+bMfV-2<*Gcre6y~G^ zH>rL-sTgasXil4pIj|6X%_P&O$f{!@tmqHWnRxoNG9n$Z;eeH?&Vuj$kiD1_Ap%T8 zQD6>F0nwb|t$`dWc?rooMDZpZwbrODISZg`=ZPk6QQ01OJu`Fbr~^uuor_c*HJS&+ z4M4$F6Fng~;#b*@fUF{D4~wrAv=`nD+Xl^BYR(0Ex6X!GxPRd-tx4@pcTCT2LT!1w z78y!vCUS3+I#~r6d+w<_avan_&DY&Z>f>Jzju!$PVah`Ql@`!g6Ikx15kllRO^;uj z#;@f#T@aC3h>?_V*v|ytv^CThXgqU>2|OTf3{=w>Oo!)bmC5%R)LcbgMz&9|P>}AF zq2fVS)FN3;!Maf0E@gauhmLSE@u;~T3Fiq=`@}Q*G)|ulDUdu*ET0%+W@5haydk<9 z-*i2L4i^jcheq3g*AW3H1~EgNd`v)XoI-rdYk(IBWOBlF>?kG>3zn4CiR^Xa$(&pU+vzrg z&7gE-ahfFN1cQWjyt?Jdc0AmRgRpx9-9U_#JF$dy?Av>&@9OpI{RIn1w$2wT`3oS4 z$>`<1@wLKQ%f#=SF>Gq`&_W;m3)*>lJjKqV{$ zaSrG?Anp`HjhCiy?aG>d{O`ySm=q_g)tQ05zPY)%-jf{7m>#$HWm7JDUm^y?fIOT^ z>I2lahlzqcWFJQp{r&F?nhuFc9>Sq%ZZgKz2XKxysD707KwWTkSY_ew$UiN>A*Ag^#t%#mAE{_?_g?x>sVJY}faxhm6ByD37Z?Y}~Xs8WzlyIZ$MT{z_?o zC+%WuxRhE)3z|NsOI4Sekg;HkZzR>i9=SNXB!J;}%BS%<2b~bpDRtQ^zI@yqjY%|? zF*|@Q`|($p6Svs|-9)yTd6B~DT9-kX8&l+vDj>;Y2@5!V!NfRGgpatLuy?z${uDPk zp_5Up!q1wkVjw-WTwSb52W{ztU<$0^9S&qe8lbY^TPS(K}e^4(q{7@;xi9H*^$UU

%Tk!T#}Hue0Bbo=nwg!- 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 + + + + 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 + إسم المجموعة + + + From d96b03e40e9c52f2838182829f0e8e4ab274e9fe Mon Sep 17 00:00:00 2001 From: Xiaokang Wang Date: Tue, 7 Jan 2014 11:02:50 +0800 Subject: [PATCH 28/55] Finished Chinese(CHS) translation --- src/translations/bitmessage_zh_cn.ts | 1645 ++++++++++++++++++++++++++ 1 file changed, 1645 insertions(+) create mode 100644 src/translations/bitmessage_zh_cn.ts diff --git a/src/translations/bitmessage_zh_cn.ts b/src/translations/bitmessage_zh_cn.ts new file mode 100644 index 00000000..9afa97ba --- /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 + 重发超时 + + + From b9da1aa33f46bc4e119e93b25ff631cabaf6a23b Mon Sep 17 00:00:00 2001 From: Xiaokang Wang Date: Tue, 7 Jan 2014 11:07:39 +0800 Subject: [PATCH 29/55] Released Chinese(CHS) translation --- src/translations/bitmessage_zh_cn.qm | Bin 0 -> 39737 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/translations/bitmessage_zh_cn.qm diff --git a/src/translations/bitmessage_zh_cn.qm b/src/translations/bitmessage_zh_cn.qm new file mode 100644 index 0000000000000000000000000000000000000000..db042d6b98083513fd3e76b94118e093d345cdbd GIT binary patch literal 39737 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 Lo|cInZV&!{$BMRr literal 0 HcmV?d00001 From 271c20e607f581731641db68bd887727823a9cea Mon Sep 17 00:00:00 2001 From: Xiaokang Wang Date: Tue, 7 Jan 2014 11:11:10 +0800 Subject: [PATCH 30/55] Created project file --- src/translations/bitmessage_zh_cn.pro | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/translations/bitmessage_zh_cn.pro 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 From e11290f6521312488b2daa3f70539bd13c691428 Mon Sep 17 00:00:00 2001 From: Xiaokang Wang Date: Tue, 7 Jan 2014 11:35:40 +0800 Subject: [PATCH 31/55] Fixed mistake in Chinese transltion --- src/translations/bitmessage_zh_cn.qm | Bin 39737 -> 39735 bytes src/translations/bitmessage_zh_cn.ts | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/translations/bitmessage_zh_cn.qm b/src/translations/bitmessage_zh_cn.qm index db042d6b98083513fd3e76b94118e093d345cdbd..2a1c681009d2fdfb8cda653080545482a43a06ae 100644 GIT binary patch delta 881 zcmX9+ZETZO6n^geao@i0x;BGJ9nuvvtk#d!5o<@{%4CwkpdCclPLU7y^=`w?IT@(K zf{ekI;aqdrB$E+-%#SrPiH%**Y?%p)i5d6-F+*p@j{+kg8Zi(f%xnB}pWJ)Td7g8g zb55qD(UjD?h-q=Bm)(d5U2JbW>dNW^AC`f*16p{M!0qwD(wuw*`+f&>yQo5wP3T&*GuJ=0trBk*ZRDpvh=(yx%PMQ|y zLdulg1p#G2;yEDjp;9#ZG|(PaN`Swj2fO(%ifo17119qjgO{(N+HP`DK81oVWH0PY&$hN6OMbZv`Lvv3OkMZQ{wE`9S4uKB@`Lst7-qm?yj}=F?$O^}ZkIy3o8#PSv4zYTdsIFQZqf zNcDSiwacM%fGwrnS}K^z-nA6+KZK%gS!KHqSaRF)OrSzAb6 z`mrbHgx0Kn#d!wsH|aNh4+w^f`rOGL;NhT=*Djc=|1s)ojsjg1M&pbRh=l&f?PrZ$ zg#n;MGDZS_i&@Rk7meL$9-_Z2E;dZ}={)TWR-wlPlKqmc4vPRJw-QpFc4U)aTg~ zlXrog-S&}E8?fbGYSmr*QiE8JLCUOoD0xcn-^9i#|E))imepoSRk5jKF_F18Ib$AL zKsUB7O!mauI1B$5LQiz8q*%vFWAlzsdRxt|w(5_P6CHK3EJYqn7Ib};DCkE)vB6 delta 875 zcmX9+YfO`86n@_C@_zl=+J+77*nrL~(ABwhk~P&tYNB)Cr3L1IO}wB2<&rYy#JRbF z;o@Z~(wu;XiTVTQB!ew!R;CH+j7yA{c_oWD7Wu(s%V3hZXqG5D$^M*^^PY2_=bY!f z0}16+LTOJGwpf}^Jc@0cmSlgA=GJQpFm#q zC%}$tu_qq$G>WS@*!euYDqLm(>Yi{(-2)17)Qv;i(t$`5MTJ`|r2WEUo)vF2bOF0_ zX;9>dnWQU?-V9~fu>qL(wK8%!1!yf*e$ze#N-C9!P8Xos^2IEcJDiq4~rp6>7e! zO|o?J5uoCC8F|_Od~j1nD{6u4KjiyES%A?e>)TnAT@CW^@-d)&t?c>b8sLq}FB8MO z=O)?v;u-GSAmeS%0`ESS0|s+$ERnZ+pJ6R?bnW?uTy!j)d_jgOQdWw9J=N%$Q_Z>UaT|{n)Z9Sjb33-TEToq7l7VTp_;<6x5i< z7EUwQggKt}6A-91ANpTngi&+)R2%S0$eP*A$YrormK_21T(nl-^8>}<|NWL*)=%dL zfhCHyKlm@-YMch8N1UfInPY}+1=Fm4%NOJ~+`>tPhTFVkyYulR%O9YEN;<9GfmHe8;*Nr>wsjtGpn{9XexF3FQ)VNoOb5*@TALxbM?1pfXX?}-m6c6 zj}AEda~#0>RH`@Ka|D#6ltcn-W04i&eEe>tW0tZ_&{XMj@y{E! zOYt_R=TcVdVi{iXj$>;JZE0N`Ki68J+N|Zl^=k3UYtr@(#l3sJOH#Fnh3 <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>版权所有 © 2012-2013 Jonathan Warren<br/>版权所有 © 2013 比特信开发者</p></body></html> @@ -1295,7 +1295,7 @@ The 'Random Number' option is selected by default but deterministic ad Passphrase - 密码 + 密钥 @@ -1614,12 +1614,12 @@ The 'Random Number' option is selected by default but deterministic ad <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> + <html><head/><body><p>您发给他们的消息默认会在网络上保存两天,之后比特信会再重发一次. 重发时间会随指数上升; 消息会在5, 10, 20... 天后重发并以此类推. 直到收到收件人的回执. 你可以在这里改变这一行为,让比特信在尝试一段时间后放弃.</p><p>留空意味着默认行为. </p></body></html> Give up after - 放弃在尝试 + @@ -1634,7 +1634,7 @@ The 'Random Number' option is selected by default but deterministic ad months. - 月。 + 月后放弃。 From 952ce2cb3c09eb9f70130710f3325ecf53908fb6 Mon Sep 17 00:00:00 2001 From: Xiaokang Wang Date: Tue, 7 Jan 2014 18:46:14 +0800 Subject: [PATCH 32/55] Fixed mistake in Chinese translation --- src/translations/bitmessage_zh_cn.qm | Bin 39735 -> 39733 bytes src/translations/bitmessage_zh_cn.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_zh_cn.qm b/src/translations/bitmessage_zh_cn.qm index 2a1c681009d2fdfb8cda653080545482a43a06ae..a0d1ad8484f4d93cfe6ab0a147146cba512d89c7 100644 GIT binary patch delta 665 zcmX9*Ye&G|#eg7N}u2Dt@hUdTz98)jGX4w{)OGBZijBnKN$ zq*VIL3MEYtwLc1lLSQ4KLeva{mL?fQMG<`8C@dH515i!Hr54e)W?7$Bo=7U)JcsF-wr{g+XM6Wn&f|QcH6w(C(1B_wwnXIqA-Ke&16?31SkPqe3w$r$|0k*9(*dE{~rKsam!IZs&ij4}>(P*?|lpWXS%9_xYYCWHWn0K{-p!4n6#r_tWYp zs}jdlDpcassnGdLUx5yRuJBnn^(eN8HtpuL4={b!ZaKE|?pG+N*y2aEiAX0;VbW%+ zI3BZTuMAw7xT-B)e$81PO4sMJgXGoQSUXMY&8(5u_4z3?Vap{afJn`dTH^t(xeV6l zJAme&hWtM6G=~|E4o?CViH3o|CUCKXs#UZ6v64UTE^R5CDdn delta 667 zcmX9*YeNhF8X1Gk|jwK0;NDJh-DWevsv3E#Q{;^FU!4w|-L#@EtN$w;bpmr6Zg{ z3d*bwJ-Yhn)y1bdh5*-IJ~QwOh*{)I9_?WcQ)!XR5^;wg z8;AnzbNur)BV%8ucV+Hqx4gBb1Zdwc@88F!2S3Sg{bcv+&d>zjMCPcIH)RzHCu(j3 zr6Iw+)eM|_BUF}F0oJd=*^kM9I3m<}Snl?EVIVsMc$~uEml?oVDctkVvuvk?Vf`f2 z7102%l~RSEi@9ohPuJOnWvY{FNe4B{?Z#Mf*)a>K{o+Of<7f9NQiLCDucSysfi%-~MQ&0+y-<7%h*D{i ztLlI&1se0KSfFJ?lRL<9j@;8 Udh+VqPS4gf<}QxqG_C0W0rfS{j{pDw diff --git a/src/translations/bitmessage_zh_cn.ts b/src/translations/bitmessage_zh_cn.ts index 9fe1d8c9..c58e9d86 100644 --- a/src/translations/bitmessage_zh_cn.ts +++ b/src/translations/bitmessage_zh_cn.ts @@ -1277,7 +1277,7 @@ The 'Random Number' option is selected by default but deterministic ad Chan bitmessage address: - 频道的比特信: + 频道的地址: From 2a80c856347abf186cbcad22a377f94ae1942200 Mon Sep 17 00:00:00 2001 From: Xiaokang Wang Date: Tue, 7 Jan 2014 19:02:41 +0800 Subject: [PATCH 33/55] Modifyed __init__.py in order to enum Chinese --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index e79af31d..e4f29284 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3320,7 +3320,7 @@ class settingsDialog(QtGui.QDialog): shared.safeConfigGetBoolean('bitmessagesettings', 'useidenticons')) global languages - languages = ['system','en','eo','fr','de','es','ru','no','en_pirate','other'] + languages = ['system','en','eo','fr','de','es','ru','no','en_pirate','zh_cn','other'] user_countrycode = str(shared.config.get('bitmessagesettings', 'userlocale')) if user_countrycode in languages: curr_index = languages.index(user_countrycode) From 9ebfd2a42f44f010fbfc0c38f332cb2a75c940b2 Mon Sep 17 00:00:00 2001 From: Xiaokang Wang Date: Tue, 7 Jan 2014 19:22:40 +0800 Subject: [PATCH 34/55] Fixed mistake in Chinese translation --- src/translations/bitmessage_zh_cn.qm | Bin 39733 -> 39733 bytes src/translations/bitmessage_zh_cn.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_zh_cn.qm b/src/translations/bitmessage_zh_cn.qm index a0d1ad8484f4d93cfe6ab0a147146cba512d89c7..dce6934795e909e521b6c72629bbcbaa515432aa 100644 GIT binary patch delta 17 ZcmdnGjcMyPrVU$4nfydHZ!4`=1prA12bur? delta 17 ZcmdnGjcMyPrVU$4nHqRDZ!4`=1prDm2gCpX diff --git a/src/translations/bitmessage_zh_cn.ts b/src/translations/bitmessage_zh_cn.ts index c58e9d86..72ebf9ae 100644 --- a/src/translations/bitmessage_zh_cn.ts +++ b/src/translations/bitmessage_zh_cn.ts @@ -1272,7 +1272,7 @@ The 'Random Number' option is selected by default but deterministic ad <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> + <html><head/><body><p>一个频道存在于一群共有同一个解密密钥的人之间。频道的密钥和比特信地址生成自可读的文字或密码(频道的名字)。要给一个频道中的每一个人发送消息,仅仅需要发送一个普通的点对点消息到频道的地址。</p><p>频道是实验性的且不受到监管。</p></body></html> From ca9827438cd67ec8b982db9c4c1ef5a8037b380e Mon Sep 17 00:00:00 2001 From: Michagogo Date: Fri, 10 Jan 2014 15:25:21 +0200 Subject: [PATCH 35/55] Fix a typo in INSTALL.md Pretty sure what's intended here is http://en.wiktionary.org/wiki/voil%C3%A0, not the musical instrument. --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From d150476e98f1c47ae642451c77ba2ec0e99bac46 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sun, 12 Jan 2014 19:30:01 -0500 Subject: [PATCH 36/55] Added comments and console output to expain that you cannot run bitmessageapi.py directly --- src/bitmessageapi.py | 15 ++++++++++++--- src/bitmessagemain.py | 22 +++++++++------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/bitmessageapi.py b/src/bitmessageapi.py index cc0008b4..7bcc6836 100644 --- a/src/bitmessageapi.py +++ b/src/bitmessageapi.py @@ -1,6 +1,15 @@ -# Copyright (c) 2012 Jonathan Warren -# Copyright (c) 2012 The Bitmessage developers -# Moved API stuff from main program to here /EM +# 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 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 diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index a5fa60dc..06e2fdaa 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -14,16 +14,6 @@ import signal # Used to capture a Ctrl-C keypress so that Bitmessage can shutdo import singleton import os - -# OSX python version check -import sys -if sys.platform == 'darwin': - if float("{1}.{2}".format(*sys.version_info)) < 7.5: - 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) - from SimpleXMLRPCServer import SimpleXMLRPCServer from bitmessageapi import MySimpleXMLRPCRequestHandler @@ -31,7 +21,6 @@ import shared from helper_sql import sqlQuery import threading - # Classes #from helper_sql import * #from class_sqlThread import * @@ -49,11 +38,19 @@ from debug import logger # Helper Functions import helper_bootstrap import helper_generic -#import proofofwork 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: + 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] = {} @@ -87,7 +84,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) From 6af92a5e09737631c09d9ca4937a26106523033b Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sun, 12 Jan 2014 19:50:44 -0500 Subject: [PATCH 37/55] fix line which keeps user from running more than once Bitmessage instance --- src/bitmessageapi.py | 1 - src/bitmessagemain.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bitmessageapi.py b/src/bitmessageapi.py index 7bcc6836..20a2e101 100644 --- a/src/bitmessageapi.py +++ b/src/bitmessageapi.py @@ -163,7 +163,6 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if addressInKeysFile != 'bitmessagesettings': status, addressVersionNumber, streamNumber, hash01 = decodeAddress( addressInKeysFile) - data # this is totally meaningless row? if len(data) > 20: data += ',' if shared.config.has_option(addressInKeysFile, 'chan'): diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 06e2fdaa..3089b052 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -94,7 +94,7 @@ class Main: def start(self, daemon=False): shared.daemon = daemon # is the application already running? If yes then exit. - singleton.singleinstance() + thisapp = singleton.singleinstance() signal.signal(signal.SIGINT, helper_generic.signal_handler) # signal.signal(signal.SIGINT, signal.SIG_DFL) From 0b81e9b206434d2cbe1b7f63c5561fe3f54c6ded Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sun, 12 Jan 2014 19:56:30 -0500 Subject: [PATCH 38/55] rename bitmessageapi.py to api.py --- src/{bitmessageapi.py => api.py} | 1823 +++++++++++++++--------------- src/bitmessagemain.py | 2 +- 2 files changed, 913 insertions(+), 912 deletions(-) rename src/{bitmessageapi.py => api.py} (98%) diff --git a/src/bitmessageapi.py b/src/api.py similarity index 98% rename from src/bitmessageapi.py rename to src/api.py index 20a2e101..bd28d923 100644 --- a/src/bitmessageapi.py +++ b/src/api.py @@ -1,911 +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 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) - +# 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 3089b052..4b210443 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -15,7 +15,7 @@ import singleton import os from SimpleXMLRPCServer import SimpleXMLRPCServer -from bitmessageapi import MySimpleXMLRPCRequestHandler +from api import MySimpleXMLRPCRequestHandler import shared from helper_sql import sqlQuery From 020fb498ca6f0c30249b28149597a2b4fc53f6d8 Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 16 Jan 2014 15:37:07 +0100 Subject: [PATCH 39/55] Update settings.py Tiny tiny change: "norsk" -> "Norsk" --- src/bitmessageqt/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index b5e0d4a7..4c20430d 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -400,7 +400,7 @@ 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", "norsk", "no")) + 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")) From 8f784522c9180b2beb0b87caac99f873ed4a40db Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 16 Jan 2014 15:38:31 +0100 Subject: [PATCH 40/55] Update settings.ui Tiny tiny change: "norsk" -> "Norsk" --- src/bitmessageqt/settings.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index 5834df7d..60945e80 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -156,7 +156,7 @@ - norsk + Norsk From 3f6b4b928943479786bc5274540ff91f365f6495 Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 16 Jan 2014 15:42:19 +0100 Subject: [PATCH 41/55] Update bitmessage_zh_cn.ts Tiny tiny change: "norsk" -> "Norsk" --- src/translations/bitmessage_zh_cn.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_zh_cn.ts b/src/translations/bitmessage_zh_cn.ts index 72ebf9ae..1812e4bf 100644 --- a/src/translations/bitmessage_zh_cn.ts +++ b/src/translations/bitmessage_zh_cn.ts @@ -1430,7 +1430,7 @@ The 'Random Number' option is selected by default but deterministic ad - norsk + Norsk no From 231c32d2822995ec2ce554c4562fb0dfe15afa0a Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 16 Jan 2014 15:43:22 +0100 Subject: [PATCH 42/55] Update bitmessage_ru.ts Tiny tiny change: "norsk" -> "Norsk" --- src/translations/bitmessage_ru.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts index 8f5088db..e67de685 100644 --- a/src/translations/bitmessage_ru.ts +++ b/src/translations/bitmessage_ru.ts @@ -1725,7 +1725,7 @@ The 'Random Number' option is selected by default but deterministic ad - norsk + Norsk no From 01ebb9ff197c5df83a569a4d22278ab7e483706b Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 16 Jan 2014 15:49:55 +0100 Subject: [PATCH 43/55] Update bitmessage_no.ts Tiny change, only a comma less --- src/translations/bitmessage_no.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_no.ts b/src/translations/bitmessage_no.ts index 9411ccb2..3087ef77 100644 --- a/src/translations/bitmessage_no.ts +++ b/src/translations/bitmessage_no.ts @@ -1594,7 +1594,7 @@ The 'Random Number' option is selected by default but deterministic ad 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. + 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. From 1ceae047afb4a5f88ea2f1c60db80915dd4a8f4f Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 16 Jan 2014 15:50:53 +0100 Subject: [PATCH 44/55] Update bitmessage_fr.ts Tiny tiny change: "norsk" -> "Norsk" --- src/translations/bitmessage_fr.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_fr.ts b/src/translations/bitmessage_fr.ts index db440660..e32eb5ef 100644 --- a/src/translations/bitmessage_fr.ts +++ b/src/translations/bitmessage_fr.ts @@ -1733,7 +1733,7 @@ L'option 'Nombre Aléatoire' est sélectionnée par défaut mais - norsk + Norsk no From 970e94616a0df96d6f66516b25398e249074d145 Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 16 Jan 2014 15:51:55 +0100 Subject: [PATCH 45/55] Update bitmessage_eo.ts Tiny tiny change: "norsk" -> "Norsk" --- src/translations/bitmessage_eo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_eo.ts b/src/translations/bitmessage_eo.ts index 963e2c54..2f09691d 100644 --- a/src/translations/bitmessage_eo.ts +++ b/src/translations/bitmessage_eo.ts @@ -1666,7 +1666,7 @@ The 'Random Number' option is selected by default but deterministic ad - norsk + Norsk no From 68f30fc6ec230381b50ecd9a2007dbb718a01e80 Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 16 Jan 2014 15:53:50 +0100 Subject: [PATCH 46/55] Update bitmessage_de.ts Tiny tiny change: "norsk" -> "Norsk" --- src/translations/bitmessage_de.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts index c7d947e9..880c9e67 100644 --- a/src/translations/bitmessage_de.ts +++ b/src/translations/bitmessage_de.ts @@ -1678,7 +1678,7 @@ Die Zufallszahlen-Option ist standard, jedoch haben deterministische Adressen ei - norsk + Norsk no From 6866349b8c717f23602dbdb9ffc895e4a2c9f77a Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 16 Jan 2014 15:54:25 +0100 Subject: [PATCH 47/55] Update bitmessage_en_pirate.ts Tiny tiny change: "norsk" -> "Norsk" --- src/translations/bitmessage_en_pirate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/bitmessage_en_pirate.ts b/src/translations/bitmessage_en_pirate.ts index 2e719bf4..ba97978a 100644 --- a/src/translations/bitmessage_en_pirate.ts +++ b/src/translations/bitmessage_en_pirate.ts @@ -1609,7 +1609,7 @@ T' 'Random Number' option be selected by default but deterministi - norsk + Norsk no From 85a409636acd838610e4d9622055ac5f08be66b8 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 16 Jan 2014 20:10:04 -0500 Subject: [PATCH 48/55] Fix #611; also swap print statements out for loggers --- src/class_objectProcessor.py | 301 +++++++++++++++++------------------ src/shared.py | 92 +++++------ 2 files changed, 186 insertions(+), 207 deletions(-) diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index 0a2f2594..20ef31ff 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -100,53 +100,45 @@ class objectProcessor(threading.Thread): readPosition += streamNumberLength if requestedAddressVersionNumber == 0: - print 'The requestedAddressVersionNumber of the pubkey request is zero. That doesn\'t make any sense. Ignoring it.' + logger.debug('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.' + logger.debug('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.' + 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: - print 'The length of the requested hash is not 20 bytes. Something is wrong. Ignoring.' + logger.debug('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') + 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: - print 'The length of the requested tag is not 32 bytes. Something is wrong. Ignoring.' + logger.debug('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') + 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 == '': - with shared.printLock: - print 'This getpubkey request is not for any of my keys.' + logger.info('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. Ignoring.\n') + 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: - with shared.printLock: - sys.stderr.write( - '(Within the recgetpubkey 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.\n') + 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'): - with shared.printLock: - print 'Ignoring getpubkey request because it is for one of my chan addresses. The other party should already have the pubkey.' + 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( @@ -154,12 +146,9 @@ class objectProcessor(threading.Thread): 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 + 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 - - 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.' + 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)) @@ -193,15 +182,14 @@ class objectProcessor(threading.Thread): data[readPosition:readPosition + 10]) readPosition += varintLength if addressVersion == 0: - print '(Within processpubkey) addressVersion of 0 doesn\'t make sense.' + logger.debug('(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.' + 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. - print '(within processpubkey) payloadLength less than 146. Sanity check failed.' + logger.debug('(within processpubkey) payloadLength less than 146. Sanity check failed.') return bitfieldBehaviors = data[readPosition:readPosition + 4] readPosition += 4 @@ -212,7 +200,7 @@ class objectProcessor(threading.Thread): readPosition += 64 publicEncryptionKey = data[readPosition:readPosition + 64] if len(publicEncryptionKey) < 64: - print 'publicEncryptionKey length less than 64. Sanity check failed.' + logger.debug('publicEncryptionKey length less than 64. Sanity check failed.') return sha = hashlib.new('sha512') sha.update( @@ -221,20 +209,25 @@ class objectProcessor(threading.Thread): 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') + 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: - print 'We HAVE used this pubkey personally. Updating time.' + logger.info('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.' + 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) @@ -242,7 +235,7 @@ class objectProcessor(threading.Thread): self.possibleNewPubkey(ripe = ripe) if addressVersion == 3: if len(data) < 170: # sanity check. - print '(within processpubkey) payloadLength less than 170. Sanity check failed.' + logger.warning('(within processpubkey) payloadLength less than 170. Sanity check failed.') return bitfieldBehaviors = data[readPosition:readPosition + 4] readPosition += 4 @@ -266,11 +259,11 @@ class objectProcessor(threading.Thread): signature = data[readPosition:readPosition + signatureLength] try: if not highlevelcrypto.verify(data[8:endOfSignedDataPosition], signature, publicSigningKey.encode('hex')): - print 'ECDSA verify failed (within processpubkey)' + logger.warning('ECDSA verify failed (within processpubkey)') return - print 'ECDSA verify passed (within processpubkey)' + logger.info('ECDSA verify passed (within processpubkey)') except Exception as err: - print 'ECDSA verify failed (within processpubkey)', err + logger.warning('ECDSA verify failed (within processpubkey) %s' % err) return sha = hashlib.new('sha512') @@ -278,20 +271,25 @@ class objectProcessor(threading.Thread): 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') - + 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: - print 'We HAVE used this pubkey personally. Updating time.' + logger.info('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.' + 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) @@ -308,15 +306,14 @@ class objectProcessor(threading.Thread): would run and handle that event quite quickly. """ if len(data) < 350: # sanity check. - print '(within processpubkey) payloadLength less than 350. Sanity check failed.' + 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: - with shared.printLock: - print 'We don\'t need this v4 pubkey. We didn\'t ask for it.' + logger.info('We don\'t need this v4 pubkey. We didn\'t ask for it.') return # Let us try to decrypt the pubkey @@ -326,8 +323,7 @@ class objectProcessor(threading.Thread): 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.' + logger.info('Pubkey decryption was unsuccessful.') return readPosition = 0 @@ -353,11 +349,11 @@ class objectProcessor(threading.Thread): signature = decryptedData[readPosition:readPosition + signatureLength] try: if not highlevelcrypto.verify(signedData, signature, publicSigningKey.encode('hex')): - print 'ECDSA verify failed (within processpubkey)' + logger.info('ECDSA verify failed (within processpubkey)') return - print 'ECDSA verify passed (within processpubkey)' + logger.info('ECDSA verify passed (within processpubkey)') except Exception as err: - print 'ECDSA verify failed (within processpubkey)', err + logger.info('ECDSA verify failed (within processpubkey) %s' % err) return sha = hashlib.new('sha512') @@ -369,17 +365,19 @@ class objectProcessor(threading.Thread): # 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.' + logger.info('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') + + 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) @@ -393,8 +391,7 @@ class objectProcessor(threading.Thread): # Display timing data timeRequiredToProcessPubkey = time.time( ) - pubkeyProcessingStartTime - with shared.printLock: - print 'Time required to process this pubkey:', timeRequiredToProcessPubkey + logger.debug('Time required to process this pubkey: %s' % timeRequiredToProcessPubkey) def processmsg(self, data): @@ -419,9 +416,7 @@ class objectProcessor(threading.Thread): initialDecryptionSuccessful = False # Let's check whether this is a message acknowledgement bound for us. if data[readPosition:] in shared.ackdataForWhichImWatching: - with shared.printLock: - print 'This msg IS an acknowledgement bound for me.' - + 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:]) @@ -429,9 +424,7 @@ class objectProcessor(threading.Thread): 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 + logger.info('This was NOT an acknowledgement bound for me.') # This is not an acknowledgement bound for me. See if it is a message @@ -442,16 +435,14 @@ class objectProcessor(threading.Thread): 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 - with shared.printLock: - print 'EC decryption successful using key associated with ripe hash:', key.encode('hex') + 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. - with shared.printLock: - print 'Length of time program spent failing to decrypt this message:', time.time() - messageProcessingStartTime, 'seconds.' + 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. @@ -462,24 +453,24 @@ class objectProcessor(threading.Thread): decryptedData[readPosition:readPosition + 10]) readPosition += messageVersionLength if messageVersion != 1: - print 'Cannot understand message versions other than one. Ignoring message.' + logger.info('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.' + logger.info('Cannot understand sendersAddressVersionNumber = 0. Ignoring message.') return if sendersAddressVersionNumber > 4: - print 'Sender\'s address version number', sendersAddressVersionNumber, 'not yet supported. Ignoring message.' + logger.info('Sender\'s address version number %s not yet supported. Ignoring message.' % sendersAddressVersionNumber) return if len(decryptedData) < 170: - print 'Length of the unencrypted data is unreasonably short. Sanity check failed. Ignoring message.' + 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: - print 'sender\'s stream number is 0. Ignoring message.' + logger.info('sender\'s stream number is 0. Ignoring message.') return readPosition += sendersStreamNumberLength behaviorBitfield = decryptedData[readPosition:readPosition + 4] @@ -494,18 +485,18 @@ class objectProcessor(threading.Thread): requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += varintLength - print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte + logger.info('sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s' % requiredAverageProofOfWorkNonceTrialsPerByte) requiredPayloadLengthExtraBytes, varintLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += varintLength - print 'sender\'s requiredPayloadLengthExtraBytes is', requiredPayloadLengthExtraBytes + 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]: - 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') + 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( @@ -530,14 +521,15 @@ class objectProcessor(threading.Thread): readPosition:readPosition + signatureLength] try: if not highlevelcrypto.verify(decryptedData[:positionOfBottomOfAckData], signature, pubSigningKey.encode('hex')): - print 'ECDSA verify failed' + logger.debug('ECDSA verify failed') return - print 'ECDSA verify passed' + logger.debug('ECDSA verify passed') except Exception as err: - print 'ECDSA verify failed', err + logger.debug('ECDSA verify failed %s' % 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.' + 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') @@ -574,8 +566,10 @@ class objectProcessor(threading.Thread): 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: + # 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') @@ -590,8 +584,7 @@ class objectProcessor(threading.Thread): '''SELECT label FROM blacklist where address=? and enabled='1' ''', fromAddress) if queryreturn != []: - with shared.printLock: - print 'Message ignored because address is in blacklist.' + logger.info('Message ignored because address is in blacklist.') blockMessage = True else: # We're using a whitelist @@ -599,7 +592,7 @@ class objectProcessor(threading.Thread): '''SELECT label FROM whitelist where address=? and enabled='1' ''', fromAddress) if queryreturn == []: - print 'Message ignored because address not in whitelist.' + logger.info('Message ignored because address not in whitelist.') blockMessage = True if not blockMessage: toLabel = shared.config.get(toAddress, 'label') @@ -608,11 +601,12 @@ class objectProcessor(threading.Thread): 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: - 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.' + 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 = '' @@ -682,9 +676,10 @@ class objectProcessor(threading.Thread): 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) + 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): @@ -708,7 +703,7 @@ class objectProcessor(threading.Thread): 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.' + 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 @@ -736,9 +731,7 @@ class objectProcessor(threading.Thread): 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() - messageProcessingStartTime - + 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 @@ -770,17 +763,16 @@ class objectProcessor(threading.Thread): signature = data[readPosition:readPosition + signatureLength] try: if not highlevelcrypto.verify(data[12:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')): - print 'ECDSA verify failed' + logger.debug('ECDSA verify failed') return - print 'ECDSA verify passed' + logger.debug('ECDSA verify passed') except Exception as err: - print 'ECDSA verify failed', err + logger.debug('ECDSA verify failed %s' % err) return # verify passed fromAddress = encodeAddress( sendersAddressVersion, sendersStream, ripe.digest()) - with shared.printLock: - print 'fromAddress:', fromAddress + 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 @@ -803,11 +795,12 @@ class objectProcessor(threading.Thread): 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: - print 'messageEncodingType == 0. Doing nothing with the message.' + logger.debug('messageEncodingType == 0. Doing nothing with the message.') else: body = 'Unknown encoding type.\n\n' + repr(message) subject = '' @@ -835,8 +828,7 @@ class objectProcessor(threading.Thread): call([apiNotifyPath, "newBroadcast"]) # Display timing data - with shared.printLock: - print 'Time spent processing this interesting broadcast:', time.time() - messageProcessingStartTime + logger.debug('Time spent processing this interesting broadcast: %s' % (time.time() - messageProcessingStartTime,)) if broadcastVersion == 2: cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint( @@ -848,16 +840,14 @@ class objectProcessor(threading.Thread): 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') + 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. - with shared.printLock: - print 'Length of time program spent failing to decrypt this v2 broadcast:', time.time() - messageProcessingStartTime, 'seconds.' - + 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. @@ -867,13 +857,13 @@ class objectProcessor(threading.Thread): 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.' + 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: - 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.' + 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] @@ -888,11 +878,11 @@ class objectProcessor(threading.Thread): requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += varintLength - print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte + logger.debug('sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s' % requiredAverageProofOfWorkNonceTrialsPerByte) requiredPayloadLengthExtraBytes, varintLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += varintLength - print 'sender\'s requiredPayloadLengthExtraBytes is', requiredPayloadLengthExtraBytes + logger.debug('sender\'s requiredPayloadLengthExtraBytes is %s' % requiredPayloadLengthExtraBytes) endOfPubkeyPosition = readPosition sha = hashlib.new('sha512') @@ -901,7 +891,7 @@ class objectProcessor(threading.Thread): 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.' + 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]) @@ -921,11 +911,11 @@ class objectProcessor(threading.Thread): readPosition:readPosition + signatureLength] try: if not highlevelcrypto.verify(decryptedData[:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')): - print 'ECDSA verify failed' + logger.debug('ECDSA verify failed') return - print 'ECDSA verify passed' + logger.debug('ECDSA verify passed') except Exception as err: - print 'ECDSA verify failed', err + logger.debug('ECDSA verify failed %s' % err) return # verify passed @@ -950,11 +940,12 @@ class objectProcessor(threading.Thread): 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: - print 'messageEncodingType == 0. Doing nothing with the message.' + logger.info('messageEncodingType == 0. Doing nothing with the message.') else: body = 'Unknown encoding type.\n\n' + repr(message) subject = '' @@ -982,8 +973,7 @@ class objectProcessor(threading.Thread): call([apiNotifyPath, "newBroadcast"]) # Display timing data - with shared.printLock: - print 'Time spent processing this interesting broadcast:', time.time() - messageProcessingStartTime + logger.info('Time spent processing this interesting broadcast: %s' % (time.time() - messageProcessingStartTime,)) if broadcastVersion == 3: cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint( @@ -992,17 +982,15 @@ class objectProcessor(threading.Thread): embeddedTag = data[readPosition:readPosition+32] readPosition += 32 if embeddedTag not in shared.MyECSubscriptionCryptorObjects: - with shared.printLock: - print 'We\'re not interested in this broadcast.' + 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:]) - print 'EC decryption successful' + logger.debug('EC decryption successful') except Exception as err: - with shared.printLock: - print 'Broadcast version 3 decryption Unsuccessful.' + logger.debug('Broadcast version 3 decryption Unsuccessful.') return signedBroadcastVersion, readPosition = decodeVarint( @@ -1011,13 +999,13 @@ class objectProcessor(threading.Thread): 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.' + 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: - 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.' + 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] @@ -1032,11 +1020,11 @@ class objectProcessor(threading.Thread): requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += varintLength - print 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is', requiredAverageProofOfWorkNonceTrialsPerByte + logger.debug('sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s' % requiredAverageProofOfWorkNonceTrialsPerByte) requiredPayloadLengthExtraBytes, varintLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += varintLength - print 'sender\'s requiredPayloadLengthExtraBytes is', requiredPayloadLengthExtraBytes + logger.debug('sender\'s requiredPayloadLengthExtraBytes is %s' % requiredPayloadLengthExtraBytes) endOfPubkeyPosition = readPosition sha = hashlib.new('sha512') @@ -1048,7 +1036,7 @@ class objectProcessor(threading.Thread): 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.' + 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]) @@ -1068,18 +1056,17 @@ class objectProcessor(threading.Thread): readPosition:readPosition + signatureLength] try: if not highlevelcrypto.verify(decryptedData[:readPositionAtBottomOfMessage], signature, sendersPubSigningKey.encode('hex')): - print 'ECDSA verify failed' + logger.debug('ECDSA verify failed') return - print 'ECDSA verify passed' + logger.debug('ECDSA verify passed') except Exception as err: - print 'ECDSA verify failed', err + logger.debug('ECDSA verify failed %s' % err) return # verify passed fromAddress = encodeAddress( sendersAddressVersion, sendersStream, calculatedRipe) - with shared.printLock: - print 'fromAddress:', fromAddress + logger.info('fromAddress: %s' % fromAddress) # Let's store the public key in case we want to reply to this person. sqlExecute( @@ -1096,11 +1083,12 @@ class objectProcessor(threading.Thread): 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: - print 'messageEncodingType == 0. Doing nothing with the message.' + logger.info('messageEncodingType == 0. Doing nothing with the message.') else: body = 'Unknown encoding type.\n\n' + repr(message) subject = '' @@ -1128,9 +1116,7 @@ class objectProcessor(threading.Thread): call([apiNotifyPath, "newBroadcast"]) # Display timing data - with shared.printLock: - print 'Time spent processing this interesting broadcast:', time.time() - messageProcessingStartTime - + 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 @@ -1139,15 +1125,14 @@ class objectProcessor(threading.Thread): # 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.' + 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: - with shared.printLock: - print 'We don\'t need this pub key. We didn\'t ask for it. Pubkey hash:', ripe.encode('hex') + 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. @@ -1156,7 +1141,7 @@ class objectProcessor(threading.Thread): 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.' + 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' ''', @@ -1165,17 +1150,17 @@ class objectProcessor(threading.Thread): def ackDataHasAVaildHeader(self, ackData): if len(ackData) < 24: - print 'The length of ackData is unreasonably short. Not sending ackData.' + logger.info('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.' + logger.info('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.' + 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. - print 'ackdata checksum wrong. Not sending ackdata.' + 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 diff --git a/src/shared.py b/src/shared.py index 196a0418..011c89d9 100644 --- a/src/shared.py +++ b/src/shared.py @@ -290,7 +290,6 @@ def isProofOfWorkSufficient( payloadLengthExtraBytes = 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 doCleanShutdown(): @@ -431,15 +430,13 @@ def decryptAndCheckPubkeyPayload(payload, address): 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[8:readPosition] # Some of the signed data is not encrypted so let's keep it for now. @@ -454,10 +451,9 @@ 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 @@ -479,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') @@ -496,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') @@ -509,7 +504,7 @@ 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): - print 'Proof of work in msg message insufficient.' + logger.debug('Proof of work in msg message insufficient.') return readPosition = 8 @@ -523,21 +518,27 @@ def checkAndShareMsgWithPeers(data): 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: - print 'The streamNumber', streamNumberAsClaimedByMsg, 'isn\'t one we are interested in.' + 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: - print 'We have already received this msg message. Ignoring.' + logger.debug('We have already received this msg message. Ignoring.') inventoryLock.release() return elif isInSqlInventory(inventoryHash): - print 'We have already received this msg message (it is stored on disk in the SQL inventory). Ignoring it.' + 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. @@ -546,8 +547,7 @@ def checkAndShareMsgWithPeers(data): objectType, streamNumberAsClaimedByMsg, data, embeddedTime,'') inventorySets[streamNumberAsClaimedByMsg].add(inventoryHash) inventoryLock.release() - with printLock: - print 'advertising inv with hash:', inventoryHash.encode('hex') + logger.debug('advertising inv with hash: %s' % inventoryHash.encode('hex')) broadcastToSendDataQueues((streamNumberAsClaimedByMsg, 'advertiseobject', inventoryHash)) # Now let's enqueue it to be processed ourselves. @@ -560,10 +560,10 @@ def checkAndShareMsgWithPeers(data): def checkAndSharegetpubkeyWithPeers(data): if not isProofOfWorkSufficient(data): - print 'Proof of work in getpubkey message insufficient.' + logger.debug('Proof of work in getpubkey message insufficient.') return if len(data) < 34: - print 'getpubkey message doesn\'t contain enough data. Ignoring.' + logger.debug('getpubkey message doesn\'t contain enough data. Ignoring.') return readPosition = 8 # bypass the nonce embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) @@ -577,10 +577,10 @@ def checkAndSharegetpubkeyWithPeers(data): readPosition += 4 if embeddedTime > int(time.time()) + 10800: - print 'The time in this getpubkey message is too new. Ignoring it. Time:', embeddedTime + logger.debug('The time in this getpubkey message is too new. Ignoring it. Time: %s' % embeddedTime) return if embeddedTime < int(time.time()) - maximumAgeOfAnObjectThatIAmWillingToAccept: - print 'The time in this getpubkey message is too old. Ignoring it. Time:', embeddedTime + logger.debug('The time in this getpubkey message is too old. Ignoring it. Time: %s' % embeddedTime) return requestedAddressVersionNumber, addressVersionLength = decodeVarint( data[readPosition:readPosition + 10]) @@ -588,7 +588,7 @@ def checkAndSharegetpubkeyWithPeers(data): streamNumber, streamNumberLength = decodeVarint( data[readPosition:readPosition + 10]) if not streamNumber in streamsInWhichIAmParticipating: - print 'The streamNumber', streamNumber, 'isn\'t one we are interested in.' + logger.debug('The streamNumber %s isn\'t one we are interested in.' % streamNumber) return readPosition += streamNumberLength @@ -596,11 +596,11 @@ def checkAndSharegetpubkeyWithPeers(data): inventoryHash = calculateInventoryHash(data) inventoryLock.acquire() if inventoryHash in inventory: - print 'We have already received this getpubkey request. Ignoring it.' + logger.debug('We have already received this getpubkey request. Ignoring it.') inventoryLock.release() return elif isInSqlInventory(inventoryHash): - print 'We have already received this getpubkey request (it is stored on disk in the SQL inventory). Ignoring it.' + logger.debug('We have already received this getpubkey request (it is stored on disk in the SQL inventory). Ignoring it.') inventoryLock.release() return @@ -610,8 +610,7 @@ def checkAndSharegetpubkeyWithPeers(data): inventorySets[streamNumber].add(inventoryHash) inventoryLock.release() # This getpubkey request is valid. Forward to peers. - with printLock: - print 'advertising inv with hash:', inventoryHash.encode('hex') + logger.debug('advertising inv with hash: %s' % inventoryHash.encode('hex')) broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) # Now let's queue it to be processed ourselves. @@ -627,7 +626,7 @@ def checkAndSharePubkeyWithPeers(data): return # Let us make sure that the proof of work is sufficient. if not isProofOfWorkSufficient(data): - print 'Proof of work in pubkey message insufficient.' + logger.debug('Proof of work in pubkey message insufficient.') return readPosition = 8 # for the nonce @@ -642,12 +641,10 @@ def checkAndSharePubkeyWithPeers(data): readPosition += 4 if embeddedTime < int(time.time()) - lengthOfTimeToHoldOnToAllPubkeys: - with printLock: - print 'The embedded time in this pubkey message is too old. Ignoring. Embedded time is:', embeddedTime + 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: - with printLock: - print 'The embedded time in this pubkey message more than several hours in the future. This is irrational. Ignoring message.' + 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]) @@ -656,11 +653,11 @@ def checkAndSharePubkeyWithPeers(data): data[readPosition:readPosition + 10]) readPosition += varintLength if not streamNumber in streamsInWhichIAmParticipating: - print 'The streamNumber', streamNumber, 'isn\'t one we are interested in.' + logger.debug('The streamNumber %s isn\'t one we are interested in.' % streamNumber) return if addressVersion >= 4: tag = data[readPosition:readPosition + 32] - print 'tag in received pubkey is:', tag.encode('hex') + logger.debug('tag in received pubkey is: %s' % tag.encode('hex')) else: tag = '' @@ -668,11 +665,11 @@ def checkAndSharePubkeyWithPeers(data): inventoryHash = calculateInventoryHash(data) inventoryLock.acquire() if inventoryHash in inventory: - print 'We have already received this pubkey. Ignoring it.' + logger.debug('We have already received this pubkey. Ignoring it.') inventoryLock.release() return elif isInSqlInventory(inventoryHash): - print 'We have already received this pubkey (it is stored on disk in the SQL inventory). Ignoring it.' + logger.debug('We have already received this pubkey (it is stored on disk in the SQL inventory). Ignoring it.') inventoryLock.release() return objectType = 'pubkey' @@ -681,8 +678,7 @@ def checkAndSharePubkeyWithPeers(data): inventorySets[streamNumber].add(inventoryHash) inventoryLock.release() # This object is valid. Forward it to peers. - with printLock: - print 'advertising inv with hash:', inventoryHash.encode('hex') + logger.debug('advertising inv with hash: %s' % inventoryHash.encode('hex')) broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) @@ -698,7 +694,7 @@ def checkAndSharePubkeyWithPeers(data): def checkAndShareBroadcastWithPeers(data): # Let us verify that the proof of work is sufficient. if not isProofOfWorkSufficient(data): - print 'Proof of work in broadcast message insufficient.' + logger.debug('Proof of work in broadcast message insufficient.') return readPosition = 8 # bypass the nonce embeddedTime, = unpack('>I', data[readPosition:readPosition + 4]) @@ -712,13 +708,13 @@ def checkAndShareBroadcastWithPeers(data): readPosition += 4 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.' + 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): - print 'The embedded time in this broadcast message is too old. Ignoring message.' + logger.debug('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.' + 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]) @@ -727,22 +723,21 @@ def checkAndShareBroadcastWithPeers(data): streamNumber, streamNumberLength = decodeVarint(data[readPosition:readPosition + 10]) readPosition += streamNumberLength if not streamNumber in streamsInWhichIAmParticipating: - print 'The streamNumber', streamNumber, 'isn\'t one we are interested in.' + logger.debug('The streamNumber %s isn\'t one we are interested in.' % streamNumber) return if broadcastVersion >= 3: tag = data[readPosition:readPosition+32] - print 'debugging. in broadcast, tag is:', tag.encode('hex') else: tag = '' shared.numberOfInventoryLookupsPerformed += 1 inventoryLock.acquire() inventoryHash = calculateInventoryHash(data) if inventoryHash in inventory: - print 'We have already received this broadcast object. Ignoring.' + logger.debug('We have already received this broadcast object. Ignoring.') inventoryLock.release() return elif isInSqlInventory(inventoryHash): - print 'We have already received this broadcast object (it is stored on disk in the SQL inventory). Ignoring it.' + 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. @@ -752,8 +747,7 @@ def checkAndShareBroadcastWithPeers(data): inventorySets[streamNumber].add(inventoryHash) inventoryLock.release() # This object is valid. Forward it to peers. - with printLock: - print 'advertising inv with hash:', inventoryHash.encode('hex') + logger.debug('advertising inv with hash: %s' % inventoryHash.encode('hex')) broadcastToSendDataQueues((streamNumber, 'advertiseobject', inventoryHash)) # Now let's queue it to be processed ourselves. From d72e9c10153c70366407c19fdd8d5cc220324408 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 20 Jan 2014 13:45:21 -0500 Subject: [PATCH 49/55] add missing import --- src/class_singleCleaner.py | 1 + 1 file changed, 1 insertion(+) 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 From 89d778528a1d9d16d020098d75fd0895b9734e75 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 20 Jan 2014 14:30:47 -0500 Subject: [PATCH 50/55] changed the Chinese translation of 'Chinese' to the simplified version --- src/bitmessageqt/settings.py | 4 ++-- src/bitmessageqt/settings.ui | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 4c20430d..293c133b 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Sun Jan 12 20:21:07 2014 +# 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! @@ -402,7 +402,7 @@ class Ui_settingsDialog(object): self.languageComboBox.setItemText(6, _translate("settingsDialog", "русский язык", "ru")) 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(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)) diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index 60945e80..2af19613 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -166,7 +166,7 @@ - 中国的 + 简体中文 From c98ca590a8fcbe65a7ce668529d2fdcea5654de6 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 20 Jan 2014 15:25:02 -0500 Subject: [PATCH 51/55] Fix #566 --- src/bitmessagemain.py | 9 ++++++--- src/helper_startup.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 4b210443..e4a073d9 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -15,7 +15,8 @@ import singleton import os from SimpleXMLRPCServer import SimpleXMLRPCServer -from api import MySimpleXMLRPCRequestHandler +from api import MySimpleXMLRPCRequestHandler +from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections import shared from helper_sql import sqlQuery @@ -59,10 +60,12 @@ def connectToStream(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) diff --git a/src/helper_startup.py b/src/helper_startup.py index 0e35e594..0bae9496 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 + return False + except Exception as err: + print 'An Exception occurred within isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections:', err + return False \ No newline at end of file From 7983a147545b254b4c8442609394dd0d9f9105aa Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 20 Jan 2014 23:04:41 -0500 Subject: [PATCH 52/55] Increment version number to 0.4.2 --- Makefile | 2 +- arch.sh | 4 ++-- archpackage/PKGBUILD | 2 +- debian.sh | 4 ++-- ebuild.sh | 4 ++-- generate.sh | 2 +- puppy.sh | 4 ++-- rpm.sh | 4 ++-- rpmpackage/pybitmessage.spec | 2 +- slack.sh | 4 ++-- src/build_osx.py | 2 +- src/shared.py | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index c234dc29..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 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/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/shared.py b/src/shared.py index 011c89d9..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. From 917e27c19b5a3425cf560ac45f5d238dc53ecb32 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 21 Jan 2014 01:17:36 -0500 Subject: [PATCH 53/55] minor change to new function isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections --- src/helper_startup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper_startup.py b/src/helper_startup.py index 0bae9496..abc4958f 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -126,7 +126,7 @@ def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections(): try: VER_THIS=StrictVersion(platform.version()) if sys.platform[0:3]=="win": - return StrictVersion("5.1.2600")VER_THIS + 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 From 8c7031f56dc37c3c9e26a80ea7142b16a5525a3d Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 21 Jan 2014 22:41:48 -0500 Subject: [PATCH 54/55] changed 2013 to 2014 in copyright notices --- COPYING | 4 ++-- LICENSE | 4 ++-- src/bitmessageqt/about.py | 6 +++--- src/bitmessageqt/about.ui | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) 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/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/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 From bbed86921baa7fb37d449043aca8d3feb253d08d Mon Sep 17 00:00:00 2001 From: flaskevann Date: Thu, 23 Jan 2014 20:51:05 +0100 Subject: [PATCH 55/55] Major update of bitmessage_no.ts I considered this file to be done already and assumed that the huge amount of mixed english and norwegian inside the gui was somebodys elses fault. But after mocking about with Python the entire day I found that I was wrong. I couldn't even get the ts file to compile into a qm to begin with, but everything should be fine now. --- src/translations/bitmessage_no.ts | 416 +++++++++++++++--------------- 1 file changed, 211 insertions(+), 205 deletions(-) diff --git a/src/translations/bitmessage_no.ts b/src/translations/bitmessage_no.ts index 3087ef77..285002a1 100644 --- a/src/translations/bitmessage_no.ts +++ b/src/translations/bitmessage_no.ts @@ -5,17 +5,17 @@ Add new entry - Legg til ny oppføring + Legg til ny oppføring Label - Etikett + Etikett Address - Adresse + Adresse
@@ -96,7 +96,7 @@ p, li { white-space: pre-wrap; } Broadcast to everyone who is subscribed to your address - Kringkast til alle som abonnerer på din adresse + Kringkast til alle som abonnerer på din adresse @@ -106,7 +106,7 @@ p, li { white-space: pre-wrap; } 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. + 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. @@ -136,7 +136,7 @@ p, li { white-space: pre-wrap; } Stream - Strøm + Strøm @@ -146,7 +146,7 @@ p, li { white-space: pre-wrap; } 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 meldinger' sendt av andre brukere. Meldingene vil vises i din innboks. Adressene her vil overstyre de på svartelistefanen. + Her kan du abonnere på 'kringkastede beskjeder' sendt av andre brukere. Beskjedene vil vises i din innboks. Adressene her vil overstyre de under svartelistefanen. @@ -166,12 +166,12 @@ p, li { white-space: pre-wrap; } 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. + 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 + Legg til ny oppføring @@ -186,12 +186,12 @@ p, li { white-space: pre-wrap; } Use a Blacklist (Allow all incoming messages except those on the Blacklist) - Bruk svarteliste (tillat beskjeder fra alle adresser unntatt de på svartelisten) + 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) + Bruk hviteliste (blokker beskjeder fra alle adresser unntatt de på hvitelisten) @@ -201,7 +201,7 @@ p, li { white-space: pre-wrap; } Stream Number - Strømnummer + Strømnummer @@ -216,17 +216,17 @@ p, li { white-space: pre-wrap; } Since startup at asdf: - Siden oppstart på asdf: + Siden oppstart på asdf: Processed 0 person-to-person message. - Har bearbeidet 0 beskjeder for person-til-person + Har bearbeidet 0 beskjeder for person-til-person. Processed 0 public key. - Har bearbeidet 0 offentlige nøkler. + Har bearbeidet 0 offentlige nøkler. @@ -256,12 +256,12 @@ p, li { white-space: pre-wrap; } Import keys - Importer inn nøkler + Importer inn nøkler Manage keys - Administrer nøkler + Administrer nøkler @@ -321,7 +321,7 @@ p, li { white-space: pre-wrap; } Processed %1 public keys. - Bearbeidet %1 offentlige nøkler. + Bearbeidet %1 offentlige nøkler. @@ -331,42 +331,42 @@ p, li { white-space: pre-wrap; } Waiting for their encryption key. Will request it again soon. - Venter på krypteringsnøkkel. Sender straks en ny forespørsel. + Venter på krypteringsnøkkel. Sender straks en ny forespørsel. Encryption key request queued. - Forespørsel for å finne krypteringsnøkkel er satt i kø. + Forespørsel for å finne krypteringsnøkkel er satt i kø. Queued. - Satt i kø. + 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ø. + Trenger å utføre arbeidsoppgave for sende beskjed. Denne er satt i kø. Acknowledgement of the message received %1 - Bekreftelse på beskjeden mottatt %1 + Bekreftelse på beskjeden mottatt %1 Broadcast queued. - Kringkasting satt i kø. + Kringkasting satt i kø. Broadcast on %1 - Kringkasting på %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 + Problem: Det nødvendige arbeidet som kreves utført av mottaker er mer krevende enn det som er satt som akseptabelt. %1 @@ -376,35 +376,35 @@ p, li { white-space: pre-wrap; } Message sent. Waiting for acknowledgement. Sent at %1 - Beskjed sendt. Venter på bekreftelse. Sendt %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. + 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 + 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 endringer lagres.) + 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 + 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 endringer lagres.) +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.) @@ -469,12 +469,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn Are you sure you want to delete all trashed messages? - Er du sikker på at du vil slette alle kastede beskjeder? + 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. + Du må skrive inn passordfrasen din. Hvis du ikke har en kan du ikke bruke dette skjemaet. @@ -484,12 +484,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn Open keys.dat? - Åpne keys.dat? + Åpne keys.dat? bad passphrase - Dårlig passordfrase + Dårlig passordfrase @@ -499,12 +499,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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. + 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. + Bitmessage vil bruke proxy fra nå av, ta en omstart hvis du vil lukke alle eksisterende tilkoblinger. @@ -524,7 +524,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn The passphrase you entered twice doesn't match. Try again. - Passordfrasene er ikke like. Vennligst prøv igjen. + Passordfrasene er ikke like. Vennligst prøv igjen. @@ -534,12 +534,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn You really do need a passphrase. - Du trenger sårt en passordfrase. + Du trenger sårt en passordfrase. All done. Closing user interface... - Ferdig. Lukker brukergrensesnittet ... + Ferdig. Lukker brukergrensesnittet... @@ -559,12 +559,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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. + 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. + Ny oppføring lagt til i adresseboka. Du kan forandre etiketten til det du måtte ønske. @@ -574,7 +574,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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. + 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. @@ -589,7 +589,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn The address should start with ''BM-'' - Adressen bør starte med ''BM-'' + Adressen bør starte med ''BM-'' @@ -599,7 +599,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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. + Typenummeret for denne adressen er høyere enn det programvaren støtter. Vennligst oppgrader Bitmessage. @@ -624,7 +624,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn You are using TCP port %1. (This can be changed in the settings). - Du benytter TCP-port %1. (Dette kan endres på i innstillingene). + Du benytter TCP-port %1. (Dette kan endres på i innstillingene). @@ -644,7 +644,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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. + 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. @@ -664,7 +664,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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. + Feil: Du må oppgi en avsenderadresse. Hvis du ikke har en gå til 'Dine identiteter'-fanen. @@ -674,7 +674,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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. + 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. @@ -684,22 +684,22 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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. + Angående adressen %1, Bitmessage forstår ikke adressetypenumre for %2. Oppdater Bitmessage til siste versjon. Stream number - Strømnummer + 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. + 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. + 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. @@ -709,12 +709,12 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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'. + 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 abonnere på samme adresse flere ganger. + Feil: Du kan ikke legge til samme adresse flere ganger i abonnementlista. @@ -724,7 +724,7 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn 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? + En av dine gamle adresser er av den første typen og derfor ikke lenger støttet: %1. Derfor kan den vel slettes? @@ -754,191 +754,191 @@ Det er viktig at du tar en sikkerhetskopi av denne filen. Vil du åpne denn Done generating address. Doing work necessary to broadcast it... - Ferdig med å generere adresse. Utfører nødvendig arbeidsoppgave for å kringkaste den ... + Ferdig med å generere adresse. Utfører nødvendig arbeidsoppgave for å kringkaste den ... Done generating address - Ferdig med å generere adresse + Ferdig med å generere adresse Message sent. Waiting on acknowledgement. Sent on %1 - Beskjed sendt, venter på bekreftelse. Sendt %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. + 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 ... + Utfører nødvendig arbeidsoppgave for å kringkaste ... Broadcast sent on %1 - Kringkastet på %1 + Kringkastet på %1 Looking up the receiver's public key - Gjør oppslag for å finne mottakers offentlige nøkkel + 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.) + 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. + 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. + 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ø. + Arbeidsoppgave er satt i kø. Work is queued. %1 - Arbeidsoppgave er satt i kø. %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. + 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 + Problem: Mottakerens nøkkel kunne ikke brukes til å kryptere beskjeden. %1 Save message as... - Lagre beskjed som ... + Lagre beskjed som ... Mark Unread - Merk som ulest + Merk som ulest Subscribe to this address - Abonner på denne adressen + Abonner på denne adressen Message sent. Sent at %1 - Beskjed sendt. Sendt %1 + Beskjed sendt. Sendt %1 Chan name needed - Kanalnavn nødvendig + Kanalnavn nødvendig You didn't enter a chan name. - Du oppga ikke noe kanalnavn. + Du oppga ikke noe kanalnavn. Address already present - Adressen eksisterer allerede + 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. + Kunne ikke legge til kanal siden den ser ut til å allerede være lagret som en av dine identiteter. Success - Suksess + 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' + 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 + 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 + 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 + Ugyldig adresse That Bitmessage address is not valid. - Bitmessage-adressen er ikke gyldig. + Bitmessage-adressen er ikke gyldig. Address does not match chan name - Adresse stemmer ikke med kanalnavnet + 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. + Selv om Bitmessage-adressen du oppga var gyldig stemmer den ikke med kanalnavnet. Successfully joined chan. - Deltar nå i kanal + Deltar nå i kanal. Fetched address from namecoin identity. - Hentet adresse fra Namecoin-identitet. + Hentet adresse fra Namecoin-identitet. New Message - Ny beskjed + Ny beskjed From - Fra + 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) + 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 ... + Lagre som ... Write error. - Skrivefeil + Skrivefeil. @@ -948,149 +948,151 @@ Det er ingen krevd vanskelighet for adresser av type to som benyttet her. Testing... - Tester ... + 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 + Dette er en kanaladresse. Du kan ikke bruke den som en pseudo-epostliste. Search - Søk + Søk All - Alle + Alle Message - Beskjed + Beskjed Fetch Namecoin ID - Hent Namecoin-id + Hent Namecoin-id Stream # - Strøm # + Strøm # Connections - Tilkoblinger + Tilkoblinger Ctrl+Q - Ctrl+Q + Ctrl+Q F1 - F1 + F1 Join / Create chan - Delta i / opprett kanal + 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. @@ -1099,12 +1101,16 @@ It is important that you back up this file. Would you like to open the file now? 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 @@ -1126,28 +1132,28 @@ p, li { white-space: pre-wrap; } 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: + 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;">Pros:<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;">Cons:<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> + <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 genererer tilfeldige tall + Opprett en adresse ved å bruke generatoren som lager tilfeldige tall Use a passphrase to make addresses - Bruk en passordfrase for å opprette adresser + 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 + Bruk ekstra tid på å få adressen(e) en eller to tegn kortere @@ -1162,7 +1168,7 @@ The 'Random Number' option is selected by default but deterministic ad In addition to your passphrase, you must remember these numbers: - I tillegg til passordfrasen må du huske disse numrene: + I tillegg til passordfrasen må du huske disse numrene: @@ -1172,12 +1178,12 @@ The 'Random Number' option is selected by default but deterministic ad Number of addresses to make based on your passphrase: - Antall adresser som skal opprettes basert på din passordfrase: + Antall adresser som skal opprettes basert på din passordfrase: Stream number: 1 - Strømnummer: 1 + Strømnummer: 1 @@ -1197,27 +1203,27 @@ The 'Random Number' option is selected by default but deterministic ad Use the most available stream - Bruk den mest tilgjengelige strømmen + 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) + (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 + Bruk samme strøm som en eksisterende adresse (saves you some bandwidth and processing power) - (sparer deg for litt båndbredde og prosesseringskraft) + (sparer deg for litt båndbredde og prosesseringskraft) Address version number: 4 - Adressetypenummer: 4 + Adressetypenummer: 4 @@ -1225,7 +1231,7 @@ The 'Random Number' option is selected by default but deterministic ad Add new entry - Legg til ny oppføring + Legg til ny oppføring @@ -1240,7 +1246,7 @@ The 'Random Number' option is selected by default but deterministic ad CheckBox - + Avkryssningsboks @@ -1248,27 +1254,27 @@ The 'Random Number' option is selected by default but deterministic ad Special Address Behavior - Spesiell adresseoppførsel + Spesiell adresseoppførsel Behave as a normal address - Oppførsel som vanlig adresse + Oppførsel som vanlig adresse Behave as a pseudo-mailing-list address - Oppførsel som adresse på pseudo-epostliste + 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). + 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: + Navnet på pseudo-epostlista: @@ -1291,22 +1297,22 @@ The 'Random Number' option is selected by default but deterministic ad Copyright © 2013 Jonathan Warren - Kopibeskyttet © 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> + <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 + Dette er betaprogramvare. - <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> + <html><head/><body><p>Kopirett © 2012-2014 Jonathan Warren<br/>Kopirett © 2013-2014 Bitmessage-utviklerne</p></body></html> @@ -1314,22 +1320,22 @@ The 'Random Number' option is selected by default but deterministic ad Bitmessage - Bitmessage + Bitmessage Bitmessage won't connect to anyone until you let it. - Bitmessage kobler ikke til noen før du lar den + Bitmessage kobler ikke til noen før du lar den. Connect now - Koble til nå + Koble til nå Let me configure special network settings first - La meg konfigurere spesielle nettverksinnstillinger først + La meg konfigurere spesielle nettverksinnstillinger først @@ -1347,7 +1353,7 @@ The 'Random Number' option is selected by default but deterministic ad 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 Wiki: + Bitmessage er et samarbeidsprosjekt, hjelp kan bli funnet på nettet i Bitmessage-wikien: @@ -1355,27 +1361,27 @@ The 'Random Number' option is selected by default but deterministic ad Icon Glossary - Ikonoversikt + Statusoversikt You have no connections with other peers. - Du har ingen tilkoblinger til likesinnede. + 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 en likesinnet, 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. + 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 i innstillingene) + 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 likesinnede og riktig konfigurert brannmur. + Du har aktive tilkoblinger til andre og riktig konfigurert brannmur. @@ -1383,42 +1389,42 @@ The 'Random Number' option is selected by default but deterministic ad Dialog - Dialog + Opprett ny kanal Create a new chan - Opprett en ny kanal + Opprett en ny kanal Join a chan - Delta i kanal + Delta i kanal Create a chan - Opprett en kanal + 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> + <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 + 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> + <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 + Bitmessage-kanaladresse: @@ -1441,7 +1447,7 @@ The 'Random Number' option is selected by default but deterministic ad Number of addresses to make based on your passphrase: - Antall adresser som skal opprettes basert på din passordfrase: + Antall adresser som skal opprettes basert på din passordfrase: @@ -1456,7 +1462,7 @@ The 'Random Number' option is selected by default but deterministic ad Stream number: - Strømnummer: + Strømnummer: @@ -1466,22 +1472,22 @@ The 'Random Number' option is selected by default but deterministic ad 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 + 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. + 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. + 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: @@ -1494,7 +1500,7 @@ The 'Random Number' option is selected by default but deterministic ad Start Bitmessage on user login - Start Bitmessage ved brukerpålogging + Start Bitmessage ved brukerpålogging @@ -1509,17 +1515,17 @@ The 'Random Number' option is selected by default but deterministic ad Show notification when message received - Vis varsel når beskjed mottas + Vis varsel når beskjed mottas Run in Portable Mode - Kjør i flyttbar modus + 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. + 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. @@ -1534,7 +1540,7 @@ The 'Random Number' option is selected by default but deterministic ad Listen for connections on port: - Lytt etter tilkoblinger på port: + Lytt etter tilkoblinger på port: @@ -1594,7 +1600,7 @@ The 'Random Number' option is selected by default but deterministic ad 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. + 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. @@ -1609,12 +1615,12 @@ The 'Random Number' option is selected by default but deterministic ad 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. + '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. + 'Total vanskelighet' påvirker den absolutte mengden av arbeid som avsender må fullføre. Dobling av denne verdien dobler også arbeidsmengden. @@ -1624,7 +1630,7 @@ The 'Random Number' option is selected by default but deterministic ad 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. + 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. @@ -1644,73 +1650,73 @@ The 'Random Number' option is selected by default but deterministic ad Willingly include unencrypted destination address when sending to a mobile device - Inkluder med vilje ukrypterte destinasjonadresser når mobil enhet er mottaker + 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'):) + 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 + 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 annen 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> + <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: + Vert: Password: - Passord: + Passord: Test - Test + Test Connect to: - Koble til: + Koble til: Namecoind - Namecoind + Namecoind NMControl - NMControl + NMControl Namecoin integration - Namecoin-integrasjon + Namecoin-integrasjon Use Identicons - Bruk identikoner + Bruk identikoner Interface Language - Grensesnittspråk + Grensesnittspråk System Settings system - Systeminnstillinger + Systeminnstillinger @@ -1764,37 +1770,37 @@ The 'Random Number' option is selected by default but deterministic ad Other (set in keys.dat) other - Annet (sett inn keys.dat) + 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å utover 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> + <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 + Gi opp etter and - og + og days - dager + dager months. - måneder. + måneder. Resends Expire - Resendinger utgår + Igjensending