self.label_3.setText(QtGui.QApplication.translate("aboutDialog","Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php",None,QtGui.QApplication.UnicodeUTF8))
self.label_4.setText(QtGui.QApplication.translate("aboutDialog","This product includes Python-RSA (http://stuvel.eu/rsa) originally written by Sybren A. Stüvel <sybren@stuvel.eu>. It is licensed under the Apache 2.0 license: http://www.apache.org/licenses/LICENSE-2.0",None,QtGui.QApplication.UnicodeUTF8))
self.label_3.setText(QtGui.QApplication.translate("aboutDialog","<html><head/><body><p>Distributed under the MIT/X11 software license, see the accompanying file license.txt or <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>",None,QtGui.QApplication.UnicodeUTF8))
self.label_5.setText(QtGui.QApplication.translate("aboutDialog","This is Beta software.",None,QtGui.QApplication.UnicodeUTF8))
<string>Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>20</x>
<y>280</y>
<width>331</width>
<height>81</height>
</rect>
</property>
<property name="text">
<string>This product includes Python-RSA (http://stuvel.eu/rsa) originally written by Sybren A. Stüvel <sybren@stuvel.eu>. It is licensed under the Apache 2.0 license: http://www.apache.org/licenses/LICENSE-2.0</string>
<string><html><head/><body><p>Distributed under the MIT/X11 software license, see the accompanying file license.txt or <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></string>
#Right now, PyBitmessage only support connecting to stream 1. It doesn't yet contain logic to expand into further streams.
softwareVersion='0.1.6'
softwareVersion='0.2.0'
verbose=2
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.
maximumAgeOfObjectsThatIAdvertiseToOthers=216000#Equals two days and 12 hours
maximumAgeOfNodesThatIAdvertiseToOthers=10800#Equals three hours
storeConfigFilesInSameDirectoryAsProgram=False
useVeryEasyProofOfWorkForTesting=False#If you set this to True while on the normal network, you won't be able to send or sometimes receive messages.
importsys
try:
@ -25,6 +26,7 @@ import ConfigParser
frombitmessageuiimport*
fromnewaddressdialogimport*
fromnewsubscriptiondialogimport*
fromregenerateaddressesimport*
fromsettingsimport*
fromaboutimport*
fromhelpimport*
@ -48,6 +50,11 @@ from time import strftime, localtime
importos
importstring
importsocks
#import pyelliptic
importhighlevelcrypto
frompyelliptic.opensslimportOpenSSL
importctypes
frompyellipticimportarithmetic
#For each stream to which we connect, one outgoingSynSender thread will exist and will create 8 connections with peers.
classoutgoingSynSender(QThread):
@ -131,7 +138,9 @@ class outgoingSynSender(QThread):
sd.sendVersionMessage()
exceptsocks.GeneralProxyError,err:
printLock.acquire()
print'Could NOT connect to',HOST,'during outgoing attempt.',err
if(int(time.time())-timeLastSeen)>172800andlen(knownNodes[self.streamNumber])>1000:# for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the knownNodes data-structure.
delknownNodes[self.streamNumber][HOST]
@ -150,7 +159,9 @@ class outgoingSynSender(QThread):
print'Bitmessage MIGHT be having trouble connecting to the SOCKS server. '+str(err)
#self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"Problem: Bitmessage can not connect to the SOCKS server. "+str(err))
else:
printLock.acquire()
print'Could NOT connect to',HOST,'during outgoing attempt.',err
if(int(time.time())-timeLastSeen)>172800andlen(knownNodes[self.streamNumber])>1000:# for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the knownNodes data-structure.
delknownNodes[self.streamNumber][HOST]
@ -182,11 +193,11 @@ class singleListener(QThread):
whileTrue:
#We don't want to accept incoming connections if the user is using a SOCKS proxy. If they eventually select proxy 'none' then this will start listening for connections.
#We don't want to accept incoming connections if the user is using a SOCKS proxy. If the user eventually select proxy 'none' then this will start listening for connections.
#Users are finding that if they run more than one node in the same network (thus with the same public IP), they can not connect with the second node. This is because this section of code won't accept the connection from the same IP. This problem will go away when the Bitmessage network grows behond being tiny but in the mean time, I'll comment out this code section.
#Users are finding that if they run more than one node in the same network (thus with the same public IP), they can not connect with the second node. This is because this section of code won't accept the connection from the same IP. This problem will go away when the Bitmessage network grows beyond being tiny but in the mean time I'll comment out this code section.
"""while HOST in connectedHostsList:
print'incoming connection is from a host in connectedHostsList (we are already connected to it). Ignoring it.'
a.close()
@ -220,10 +231,10 @@ class receiveDataThread(QThread):
self.payloadLength=0#This is the protocol payload length thus it doesn't include the 24 byte message header
self.receivedgetbiginv=False#Gets set to true once we receive a getbiginv message from our peer. An abusive peer might request it too much so we use this variable to check whether they have already asked for a big inv message.
self.objectsThatWeHaveYetToGet={}
connectedHostsList[self.HOST]=0
connectedHostsList[self.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 the 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.
ifself.streamNumber==-1:#This was an incoming connection. Send out a version message if we accept the other node's version message.
self.initiatedConnection=False
@ -239,7 +250,7 @@ class receiveDataThread(QThread):
self.data=self.data+self.sock.recv(65536)
exceptsocket.timeout:
printLock.acquire()
print'Timeout occurred waiting for data. Closing thread.'
print'Timeout occurred waiting for data. Closing receiveData thread.'
printLock.release()
break
exceptException,err:
@ -250,7 +261,7 @@ class receiveDataThread(QThread):
ifself.connectionIsOrWasFullyEstablished:#We don't want to decrement the number of connections and show the result if we never incremented it in the first place (which we only do if the connection is fully established- meaning that both nodes accepted each other's version packets.)
print'Inventory (in memory) already has object listed in inv message.'
printLock.release()
delself.objectsThatWeHaveYetToGet[objectHash]
elifisInSqlInventory(objectHash):
printLock.acquire()
print'Inventory (SQL on disk) already has object listed in inv message.'
printLock.release()
delself.objectsThatWeHaveYetToGet[objectHash]
else:
print'processData function making request for object:',repr(objectHash)
#print 'processData function making request for object:', objectHash.encode('hex')
self.sendgetdata(objectHash)
delself.objectsThatWeHaveYetToGet[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.
break
@ -396,9 +416,11 @@ class receiveDataThread(QThread):
print'broadcasting addr from within connectionFullyEstablished function.'
printLock.release()
self.broadcastaddr([(int(time.time()),self.streamNumber,1,self.HOST,remoteNodeIncomingPort)])#This lets all of our peers know about this new node.
self.sendaddr()#This is one addr message to this one peer.
self.sendaddr()#This is one large addr message to this one peer.
ifconnectionsCount[self.streamNumber]>150:
printLock.acquire()
print'We are connected to too many people. Closing connection.'
printLock.release()
self.sock.close()
return
self.sendBigInv()
@ -421,18 +443,19 @@ class receiveDataThread(QThread):
forrowinqueryreturn:
hash,=row
bigInvList[hash]=0
#print 'bigInvList:', bigInvList
#We also have messages in our inventory in memory (which is a python dictionary). Let's fetch those too.
#Now let us start appending all of these hashes together. They will be sent out in a big inv message to our new peer.
forhash,storedValueinbigInvList.items():
payload+=hash
numberOfObjectsInInvMessage+=1
ifnumberOfObjectsInInvMessage>=50000:#We can only send a max of 50000 items per inv message but we may have more objects to advertise. They must be split up into multiple inv messages.
#Cannot decode senderAddressVersion higher than 1. Assuming the sender isn\' being silly, you should upgrade Bitmessage because this message shall be ignored.
#Cannot decode senderAddressVersion higher than 2. Assuming the sender isn\' being silly, you should upgrade Bitmessage because this message shall be ignored.
#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.
#You may notice that this signature doesn't cover any information that identifies the RECEIVER of the message. This makes it vulnerable to a malicious receiver Bob forwarding the message from Alice to Charlie, making it look like Alice sent the message to Charlie. This will be fixed in the next version.
#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.
#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.
print'The stream number encoded in this msg ('+streamNumberAsClaimedByMsg+') message does not match the stream number on which it was received. Ignoring it.'
print'We have already received this msg message. Ignoring.'
@ -626,7 +720,7 @@ class receiveDataThread(QThread):
sqlReturnQueue.get()
sqlLock.release()
self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),self.data[readPosition:24+self.payloadLength],'Acknowledgement of the message received just now.')
flushInventory()#so that we won't accidentially receive this message twice if the user restarts Bitmessage soon.
flushInventory()#so that we won't accidentially receive this message twice if the user restarts Bitmessage both soon and un-cleanly.
return
else:
printLock.acquire()
@ -635,10 +729,199 @@ class receiveDataThread(QThread):
printLock.release()
#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.
print'As a matter of intellectual curiosity, here is the Bitcoin address associated with the keys owned by the other person:',calculateBitcoinAddressFromPubkey(pubSigningKey),' ..and here is the testnet address:',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.'
printLock.release()
#calculate the fromRipe.
sha=hashlib.new('sha512')
sha.update(pubSigningKey+pubEncryptionKey)
ripe=hashlib.new('ripemd160')
ripe.update(sha.digest())
#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.)
t=(ripe.digest(),False,'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'+'\xFF\xFF\xFF\xFF'+data[messageVersionLength:endOfThePublicKeyPosition],int(time.time())+2419200)#after one month we may remove this pub key from our database. (2419200 = a month)
sqlLock.acquire()
sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''')
sqlSubmitQueue.put(t)
sqlReturnQueue.get()
sqlLock.release()
blockMessage=False#Gets set to True if the user shouldn't see the message according to black or white lists.
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.'
#Now let's send the acknowledgement. We'll need to make sure that our client will properly process the ackData; if the packet is malformed, we could clear out self.data and an attacker could use that behavior to determine that we were capable of decoding this message.
ackDataValidThusFar=True
iflen(ackData)<24:
print'The length of ackData is unreasonably short. Not sending ackData.'
ackDataValidThusFar=False
elifackData[0:4]!='\xe9\xbe\xb4\xd9':
print'Ackdata magic bytes were wrong. Not sending ackData.'
ackDataValidThusFar=False
ifackDataValidThusFar:
ackDataPayloadLength,=unpack('>L',ackData[16:20])
iflen(ackData)-24!=ackDataPayloadLength:
print'ackData payload length doesn\'t match the payload length specified in the header. Not sending ackdata.'
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.
#print 'self.data after:', repr(self.data)
#This section is for my RSA keys (version 1 addresses). If we don't have any version 1 addresses, then it won't matter.
#The initial decryption passed though there is a small chance that the message isn't actually for me. We'll need to check that the 20 zeros are present.
@ -669,190 +952,160 @@ class receiveDataThread(QThread):
#Let's store the public key in case we want to reply to this person.
#We don't have the correct nonce in order to 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.)
t=(ripe.digest(),False,'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'+data[20+messageVersionLength:endOfThePublicKeyPosition],int(time.time())+2419200)#after one month we may remove this pub key from our database. (2419200 = a month)
sqlLock.acquire()
sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''')
sqlSubmitQueue.put(t)
sqlReturnQueue.get()
sqlLock.release()
blockMessage=False#Gets set to True if the user shouldn't see the message according to black or white lists.
#Let's store the public key in case we want to reply to this person.
#We don't have the correct nonce in order to 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.)
t=(ripe.digest(),False,'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'+data[20+messageVersionLength:endOfThePublicKeyPosition],int(time.time())+2419200)#after one month we may remove this pub key from our database. (2419200 = a month)
sqlLock.acquire()
sqlSubmitQueue.put('''SELECT label, enabled FROM whitelist where address=?''')
sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''')
sqlSubmitQueue.put(t)
queryreturn=sqlReturnQueue.get()
sqlLock.release()
ifqueryreturn==[]:
print'Message ignored because address not in whitelist.'
blockMessage=True
forrowinqueryreturn:#It could be in the whitelist but disabled. Let's check.
label,enabled=row
ifnotenabled:
print'Message ignored because address in whitelist but not enabled.'
blockMessage=True
sqlReturnQueue.get()
sqlLock.release()
ifnotblockMessage:
print'fromAddress:',fromAddress
print'First 150 characters of message:',repr(message[:150])
#Look up the destination address (my address) based on the destination ripe hash.
#I realize that I could have a data structure devoted to this task, or maintain an indexed table
#in the sql database, but I would prefer to minimize the number of data structures this program
#uses. Searching linearly through the user's short list of addresses doesn't take very long anyway.
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.'
#print 'The POW is strong enough that this ackdataPayload will be accepted by the Bitmessage network.'
#Currently PyBitmessage only supports sending a message with the acknowledgement in the form of a msg message. But future versions, and other clients, could send any object and this software will relay them. This can be used to relay identifying information, like your public key, through another Bitmessage host in case you believe that your Internet connection is being individually watched. You may pick a random address, hope its owner is online, and send a message with encoding type 0 so that they ignore the message but send your acknowledgement data over the network. If you send and receive many messages, it would also be clever to take someone else's acknowledgement data and use it for your own. Assuming that your message is delivered successfully, both will be acknowledged simultaneously (though if it is not delivered successfully, you will be in a pickle.)
#print 'self.data before:', repr(self.data)
#We'll need to make sure that our client will properly process the ackData; if the packet is malformed, we could clear out self.data and an attacker could use that behavior to determine that we were capable of decoding this message.
ackDataValidThusFar=True
iflen(ackData)<24:
print'The length of ackData is unreasonably short. Not sending ackData.'
ackDataValidThusFar=False
ifackData[0:4]!='\xe9\xbe\xb4\xd9':
print'Ackdata magic bytes were wrong. Not sending ackData.'
ackDataValidThusFar=False
ifackDataValidThusFar:
ackDataPayloadLength,=unpack('>L',ackData[16:20])
iflen(ackData)-24!=ackDataPayloadLength:
print'ackData payload length doesn\'t match the payload length specified in the header. Not sending ackdata.'
ifqueryreturn==[]:
print'Message ignored because address not in whitelist.'
blockMessage=True
forrowinqueryreturn:#It could be in the whitelist but disabled. Let's check.
label,enabled=row
ifnotenabled:
print'Message ignored because address in whitelist but not enabled.'
blockMessage=True
ifnotblockMessage:
print'fromAddress:',fromAddress
print'First 150 characters of message:',repr(message[:150])
#Look up the destination address (my address) based on the destination ripe hash.
#I realize that I could have a data structure devoted to this task, or maintain an indexed table
#in the sql database, but I would prefer to minimize the number of data structures this program
#uses. Searching linearly through the user's short list of addresses doesn't take very long anyway.
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.'
#We'll need to make sure that our client will properly process the ackData; if the packet is malformed, it might cause us to clear out self.data and an attacker could use that behavior to determine that we decoded this message.
ackDataValidThusFar=True
iflen(ackData)<24:
print'The length of ackData is unreasonably short. Not sending ackData.'
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.
#inventory[inventoryHash] = (objectType, self.streamNumber, ackData[24:], embeddedTime) #We should probably be storing the embeddedTime of the ackData, not the embeddedTime of the original incoming msg message, but this is probably close enough.
#print 'sending the inv for the msg which is actually an acknowledgement (within sendmsg function)'
#inventory[inventoryHash] = (objectType, self.streamNumber, ackData[24:], embeddedTime) #We should probably be storing the embeddedTime of the ackData, not the embeddedTime of the original incoming msg message, but this is probably close enough.
#print 'sending the inv for the getpubkey which is actually an acknowledgement (within sendmsg function)'
#inventory[inventoryHash] = (objectType, self.streamNumber, ackData[24:], embeddedTime) #We should probably be storing the embeddedTime of the ackData, not the embeddedTime of the original incoming msg message, but this is probably close enough.
#print 'sending the inv for a pubkey which is actually an acknowledgement (within sendmsg function)'
#inventory[inventoryHash] = (objectType, self.streamNumber, ackData[24:], embeddedTime) #We should probably be storing the embeddedTime of the ackData, not the embeddedTime of the original incoming msg message, but this is probably close enough.
#print 'sending the inv for a broadcast which is actually an acknowledgement (within sendmsg function)'
#print 'ACK POW not strong enough to be accepted by the Bitmessage network.'
ifackData[0:4]!='\xe9\xbe\xb4\xd9':
print'Ackdata magic bytes were wrong. Not sending ackData.'
ackDataValidThusFar=False
ifackDataValidThusFar:
ackDataPayloadLength,=unpack('>L',ackData[16:20])
iflen(ackData)-24!=ackDataPayloadLength:#This ackData includes the protocol header which is not counted in the payload length.
print'ackData payload length doesn\'t match the payload length specified in the header. Not sending ackdata.'
ackDataValidThusFar=False
ifackDataValidThusFar:
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.
else:
print'This program cannot decode messages from addresses with versions higher than 1. Ignoring.'
statusbar='This program cannot decode messages from addresses with versions higher than 1. Ignoring it.'
print'Error: Cannot decode incoming msg versions higher than 1. Assuming the sender isn\' being silly, you should upgrade Bitmessage because this message shall be ignored.'
statusbar='Error: Cannot decode incoming msg versions higher than 1. Assuming the sender isn\' being silly, you should upgrade Bitmessage because this message shall be ignored.'
statusbar='Error: Cannot decode incoming msg versions higher than 1. Assuming the sender isn\' being silly, you should upgrade Bitmessage. Ignoring message.'
#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.
sqlSubmitQueue.put('SELECT * FROM pubkeys WHERE hash=?')
sqlSubmitQueue.put(t)
queryreturn=sqlReturnQueue.get()
#print 'queryreturn', queryreturn
elifaddressVersionNumber>2:
print'The addressVersionNumber of the pubkey request is too high. Can\'t understand. Ignoring it.'
return
print'the hash requested in this getpubkey request is:',self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength].encode('hex')
ifqueryreturn==[]:
print'pubkey request is for me but the pubkey is not in our database of pubkeys. Making it.'