From da7c1514649611074e8bfc4ad2c8ab776d22670b Mon Sep 17 00:00:00 2001 From: Justus Ranvier Date: Tue, 4 Jun 2013 15:13:16 -0500 Subject: [PATCH 1/8] Update Makefile to correct sandbox violations when built via Portage (Gentoo) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6f415786..6485c2c9 100755 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ install: install -m 644 src/pyelliptic/*.py $(DEST_APP)/pyelliptic install -m 644 src/socks/*.py $(DEST_APP)/socks install -m 644 src/bitmessageqt/*.py $(DEST_APP)/bitmessageqt - install -m 755 debian/pybm /usr/bin/pybitmessage + install -m 755 debian/pybm $(DESTDIR)/usr/bin/$(APP) install -m 644 desktop/$(APP).desktop $(DEST_SHARE)/applications/$(APP).desktop install -m 644 src/images/can-icon-24px.png $(DEST_SHARE)/icons/hicolor/24x24/apps/$(APP).png -- 2.45.1 From 354c82d85b68ca155a5ae83b3f04b602e77505a4 Mon Sep 17 00:00:00 2001 From: Justus Ranvier Date: Tue, 4 Jun 2013 15:21:53 -0500 Subject: [PATCH 2/8] Ensure $(DESTDIR)/usr/bin exists --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 6485c2c9..d49f63fc 100755 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ source: tar -cvzf ../$(APP)_$(VERSION).orig.tar.gz ../$(APP)-$(VERSION) --exclude-vcs install: + mkdir -m 755 -p $(DESTDIR)/usr/bin mkdir -m 755 -p $(DEST_APP) mkdir -m 755 -p $(DEST_SHARE)/applications mkdir -m 755 -p $(DEST_APP)/images -- 2.45.1 From 41792697b63dfd1de242ac04d691e1ea01708b12 Mon Sep 17 00:00:00 2001 From: Justus Ranvier Date: Tue, 4 Jun 2013 15:32:14 -0500 Subject: [PATCH 3/8] Add missing trailing semicolons to pybitmessage.desktop --- desktop/pybitmessage.desktop | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/desktop/pybitmessage.desktop b/desktop/pybitmessage.desktop index 363908dd..2b1b6902 100644 --- a/desktop/pybitmessage.desktop +++ b/desktop/pybitmessage.desktop @@ -7,12 +7,12 @@ Comment=Send encrypted messages to another person or to many subscribers Exec=pybitmessage %U Icon=pybitmessage Terminal=false -Categories=Network;Email;Application -Keywords=Email;E-mail;Newsgroup;Messaging +Categories=Network;Email;Application; +Keywords=Email;E-mail;Newsgroup;Messaging; X-MessagingMenu-UsesChatSection=true X-Ubuntu-Gettext-Domain=pybitmessage -Actions=Send;Subscribe;AddressBook +Actions=Send;Subscribe;AddressBook; [Desktop Action Send] Name=Send @@ -27,4 +27,4 @@ OnlyShowIn=Unity; [Desktop Action AddressBook] Name=Address Book Exec=pybitmessage -a -OnlyShowIn=Unity; \ No newline at end of file +OnlyShowIn=Unity; -- 2.45.1 From 7b508884e3c4b8b67009e887ca9ef3902ec4ad8c Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 10 Jun 2013 09:40:51 -0400 Subject: [PATCH 4/8] some initial work completed on MaxAcceptableDifficulty checking --- src/bitmessagemain.py | 71 ++++++++++++++++++++++-------------- src/bitmessageqt/__init__.py | 14 ++++--- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index f9f0950c..0c85f339 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -1263,7 +1263,14 @@ class receiveDataThread(threading.Thread): def processpubkey(self,data): readPosition = 8 #for the nonce embeddedTime, = unpack('>I',data[readPosition:readPosition+4]) - readPosition += 4 #for the time + + #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]) @@ -1461,23 +1468,6 @@ class receiveDataThread(threading.Thread): return print 'the hash requested in this getpubkey request is:', requestedHash.encode('hex') - """shared.sqlLock.acquire() - t = (requestedHash,int(time.time())-lengthOfTimeToHoldOnToAllPubkeys) #this prevents SQL injection - shared.sqlSubmitQueue.put('''SELECT hash, transmitdata, time FROM pubkeys WHERE hash=? AND havecorrectnonce=1 AND time>?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() - if queryreturn != []: - for row in queryreturn: - hash, payload, timeEncodedInPubkey = row - shared.printLock.acquire() - print 'We have the requested pubkey stored in our database of pubkeys. Sending it.' - shared.printLock.release() - inventoryHash = calculateInventoryHash(payload) - objectType = 'pubkey' - shared.inventory[inventoryHash] = (objectType, self.streamNumber, payload, timeEncodedInPubkey)#If the time embedded in this pubkey is more than 3 days old then this object isn't going to last very long in the inventory- the cleanerThread is going to come along and move it from the inventory in memory to the SQL inventory and then delete it from the SQL inventory. It should still find its way back to the original requestor if he is online however. - self.broadcastinv(inventoryHash)""" - #else: #the pubkey is not in our database of pubkeys. Let's check if the requested key is ours (which would mean we should do the POW, put it in the pubkey table, and broadcast out the pubkey.) if requestedHash in shared.myAddressesByHash: #if this address hash is one of mine if decodeAddress(shared.myAddressesByHash[requestedHash])[1] != requestedAddressVersionNumber: shared.printLock.acquire() @@ -2406,6 +2396,11 @@ class sqlThread(threading.Thread): shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte',str(shared.networkDefaultProofOfWorkNonceTrialsPerByte)) shared.config.set('bitmessagesettings','defaultpayloadlengthextrabytes',str(shared.networkDefaultPayloadLengthExtraBytes)) shared.config.set('bitmessagesettings','settingsversion','5') + + if shared.config.getint('bitmessagesettings','settingsversion') == 5: + shared.config.set('bitmessagesettings','maxacceptablenoncetrialsperbyte','0') + shared.config.set('bitmessagesettings','maxacceptablepayloadlengthextrabytes','0') + shared.config.set('bitmessagesettings','settingsversion','6') with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) @@ -3018,8 +3013,8 @@ class singleWorker(threading.Thread): shared.sqlSubmitQueue.put((toripe,)) queryreturn = shared.sqlReturnQueue.get() shared.sqlLock.release() - if queryreturn == []: - #We no longer have the needed pubkey + if queryreturn == [] and toripe not in neededPubkeys: + #We no longer have the needed pubkey and we haven't requested it. shared.printLock.acquire() sys.stderr.write('For some reason, the status of a message in our outbox is \'doingmsgpow\' even though we lack the pubkey. Here is the RIPE hash of the needed pubkey: %s\n' % toripe.encode('hex')) shared.printLock.release() @@ -3032,14 +3027,14 @@ class singleWorker(threading.Thread): shared.sqlLock.release() shared.UISignalQueue.put(('updateSentItemStatusByHash',(toripe,'Sending a request for the recipient\'s encryption key.'))) self.requestPubKey(toaddress) - return + continue ackdataForWhichImWatching[ackdata] = 0 toStatus,toAddressVersionNumber,toStreamNumber,toHash = decodeAddress(toaddress) fromStatus,fromAddressVersionNumber,fromStreamNumber,fromHash = decodeAddress(fromaddress) shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send the message.'))) shared.printLock.acquire() print 'Found a message in our database that needs to be sent with this pubkey.' - print 'First 150 characters of message:', message[:150] + print 'First 150 characters of message:', repr(message[:150]) shared.printLock.release() embeddedTime = pack('>I',(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. if fromAddressVersionNumber == 2: @@ -3140,7 +3135,15 @@ class singleWorker(threading.Thread): #The pubkey is stored the way we originally received it which means that we need to read beyond things like the nonce and time to get to the public keys. readPosition = 8 #to bypass the nonce - readPosition += 4 #to bypass the embedded time + + pubkeyEmbeddedTime, = unpack('>I',pubkeyPayload[readPosition:readPosition+4]) + #This section is used for the transition from 32 bit time to 64 bit time in the protocol. + if pubkeyEmbeddedTime == 0: + pubkeyEmbeddedTime, = unpack('>Q',pubkeyPayload[readPosition:readPosition+8]) + readPosition += 8 + else: + readPosition += 4 + readPosition += 1 #to bypass the address version whose length is definitely 1 streamNumber, streamNumberLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) readPosition += streamNumberLength @@ -3162,7 +3165,19 @@ class singleWorker(threading.Thread): requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes: requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes - #todo: pull yet-to-be-added values out of config: maximumacceptabletotaldifficult and maximumacceptablesmallmessagedifficulty and compare. + if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') != 0): + #The demanded difficulty is more than we are willing to do. + shared.sqlLock.acquire() + t = (ackdata,) + shared.sqlSubmitQueue.put('''UPDATE sent SET status='toodifficult' WHERE ackdata=? AND status='doingmsgpow' ''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Problem: The work demanded by the recipient (' + str(requiredAverageProofOfWorkNonceTrialsPerByte / shared.networkDefaultProofOfWorkNonceTrialsPerByte) + ' and ' + str(requiredPayloadLengthExtraBytes / shared.networkDefaultPayloadLengthExtraBytes) + ') is more difficult than you are willing to do. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))) + continue + + encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex')) #We are now dropping the unencrypted data in payload since it has already been encrypted and replacing it with the encrypted payload that we will send out. @@ -3189,7 +3204,7 @@ class singleWorker(threading.Thread): print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash)) - #Update the status of the message in the 'sent' table to have a 'sent' status + #Update the status of the message in the 'sent' table to have a 'msgsent' status shared.sqlLock.acquire() t = ('msgsent',toaddress, fromaddress, subject, message,'doingmsgpow') shared.sqlSubmitQueue.put('UPDATE sent SET status=? WHERE toaddress=? AND fromaddress=? AND subject=? AND message=? AND status=?') @@ -3970,7 +3985,7 @@ if __name__ == "__main__": except: #This appears to be the first time running the program; there is no config file (or it cannot be accessed). Create config file. shared.config.add_section('bitmessagesettings') - shared.config.set('bitmessagesettings','settingsversion','5') + shared.config.set('bitmessagesettings','settingsversion','6') shared.config.set('bitmessagesettings','port','8444') shared.config.set('bitmessagesettings','timeformat','%%a, %%d %%b %%Y %%I:%%M %%p') shared.config.set('bitmessagesettings','blackwhitelist','black') @@ -3992,6 +4007,8 @@ if __name__ == "__main__": shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte',str(shared.networkDefaultProofOfWorkNonceTrialsPerByte)) shared.config.set('bitmessagesettings','defaultpayloadlengthextrabytes',str(shared.networkDefaultPayloadLengthExtraBytes)) shared.config.set('bitmessagesettings','minimizeonclose','false') + shared.config.set('bitmessagesettings','maxacceptablenoncetrialsperbyte','0') + shared.config.set('bitmessagesettings','maxacceptablepayloadlengthextrabytes','0') if storeConfigFilesInSameDirectoryAsProgramByDefault: #Just use the same directory as the program and forget about the appdata folder @@ -4028,7 +4045,7 @@ if __name__ == "__main__": pickleFile = open(shared.appdata + 'knownnodes.dat', 'rb') shared.knownNodes = pickle.load(pickleFile) pickleFile.close() - if shared.config.getint('bitmessagesettings', 'settingsversion') > 5: + if shared.config.getint('bitmessagesettings', 'settingsversion') > 6: print 'Bitmessage cannot read future versions of the keys file (keys.dat). Run the newer version of Bitmessage.' raise SystemExit diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 6617da3d..94a525fb 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -363,8 +363,10 @@ class MyForm(QtGui.QMainWindow): newItem = myTableWidgetItem('Broadcast queued.') elif status == 'broadcastsent': newItem = myTableWidgetItem('Broadcast on ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) + elif status == 'toodifficult': + newItem = myTableWidgetItem('Problem: The work demanded by the recipient is more difficult than you are willing to do. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) else: - newItem = myTableWidgetItem('Unknown status. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) + newItem = myTableWidgetItem('Unknown status: ' + status + ' ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) newItem.setData(Qt.UserRole,QByteArray(ackdata)) newItem.setData(33,int(lastactiontime)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) @@ -1668,13 +1670,15 @@ 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()) - if toAddressAtCurrentInboxRow == self.str_broadcast_subscribers: self.ui.labelFrom.setText('') + elif not shared.config.has_section(toAddressAtCurrentInboxRow): + QtGui.QMessageBox.information(self, 'Address is gone','Bitmessage cannot find your address ' + toAddressAtCurrentInboxRow + '. Perhaps you removed it?', QMessageBox.Ok) + self.ui.labelFrom.setText('') + elif not shared.config.getboolean(toAddressAtCurrentInboxRow,'enabled'): + QtGui.QMessageBox.information(self, '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.', QMessageBox.Ok) + self.ui.labelFrom.setText('') else: - if not shared.config.get(toAddressAtCurrentInboxRow,'enabled'): - self.statusBar().showMessage('Error: The address from which you are trying to send is disabled. Enable it from the \'Your Identities\' tab first.') - return self.ui.labelFrom.setText(toAddressAtCurrentInboxRow) self.ui.lineEditTo.setText(str(fromAddressAtCurrentInboxRow)) self.ui.comboBoxSendFrom.setCurrentIndex(0) -- 2.45.1 From 179723bbf84da847fc02f501c05a907af259e2c3 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 10 Jun 2013 18:53:15 -0400 Subject: [PATCH 5/8] Continued Max acceptable difficulty programming and testing --- src/bitmessagemain.py | 276 ++++++++++++++++------------------- src/bitmessageqt/__init__.py | 129 ++++++++++++---- src/settings.py | 51 ++++++- src/settings.ui | 120 ++++++++++++++- src/shared.py | 10 +- 5 files changed, 399 insertions(+), 187 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 0c85f339..eddd8001 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -60,8 +60,9 @@ class outgoingSynSender(threading.Thread): time.sleep(1) global alreadyAttemptedConnectionsListResetTime while True: - #time.sleep(999999)#I sometimes use this to prevent connections for testing. - if len(selfInitiatedConnections[self.streamNumber]) < 8: #maximum number of outgoing connections = 8 + if len(selfInitiatedConnections[self.streamNumber]) >= 8: #maximum number of outgoing connections = 8 + time.sleep(10) + else: random.seed() HOST, = random.sample(shared.knownNodes[self.streamNumber], 1) alreadyAttemptedConnectionsListLock.acquire() @@ -123,7 +124,6 @@ class outgoingSynSender(threading.Thread): sock.connect((HOST, PORT)) rd = receiveDataThread() rd.daemon = True # close the main program even if there are threads left - #self.emit(SIGNAL("passObjectThrough(PyQt_PyObject)"),rd) objectsOfWhichThisRemoteNodeIsAlreadyAware = {} rd.setup(sock,HOST,PORT,self.streamNumber,objectsOfWhichThisRemoteNodeIsAlreadyAware) rd.start() @@ -150,19 +150,15 @@ class outgoingSynSender(threading.Thread): print 'deleting ', HOST, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.' shared.printLock.release() except socks.Socks5AuthError, err: - #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"SOCKS5 Authentication problem: "+str(err)) shared.UISignalQueue.put(('updateStatusBar',"SOCKS5 Authentication problem: "+str(err))) except socks.Socks5Error, err: pass print 'SOCKS5 error. (It is possible that the server wants authentication).)' ,str(err) - #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"SOCKS5 error. Server might require authentication. "+str(err)) except socks.Socks4Error, err: print 'Socks4Error:', err - #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"SOCKS4 error: "+str(err)) except socket.error, err: if shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS': 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: if verbose >= 1: shared.printLock.acquire() @@ -178,7 +174,7 @@ class outgoingSynSender(threading.Thread): shared.printLock.release() except Exception, err: sys.stderr.write('An exception has occurred in the outgoingSynSender thread that was not caught by other exception types: %s\n' % err) - time.sleep(0.1) + time.sleep(0.1) #Only one singleListener thread will ever exist. It creates the receiveDataThread and sendDataThread for each incoming connection. Note that it cannot set the stream number because it is not known yet- the other node will have to tell us its stream number in a version message. If we don't care about their stream, we will close the connection (within the recversion function of the recieveData thread) class singleListener(threading.Thread): @@ -449,7 +445,6 @@ class receiveDataThread(threading.Thread): def connectionFullyEstablished(self): self.connectionIsOrWasFullyEstablished = True if not self.initiatedConnection: - #self.emit(SIGNAL("setStatusIcon(PyQt_PyObject)"),'green') shared.UISignalQueue.put(('setStatusIcon','green')) self.sock.settimeout(600) #We'll send out a pong every 5 minutes to make sure the connection stays alive if there has been no other traffic to send lately. shared.UISignalQueue.put(('updateNetworkStatusTab','no data')) @@ -569,7 +564,6 @@ class receiveDataThread(threading.Thread): shared.inventory[self.inventoryHash] = (objectType, self.streamNumber, data, embeddedTime) shared.inventoryLock.release() self.broadcastinv(self.inventoryHash) - #self.emit(SIGNAL("incrementNumberOfBroadcastsProcessed()")) shared.UISignalQueue.put(('incrementNumberOfBroadcastsProcessed','no data')) @@ -700,7 +694,6 @@ class receiveDataThread(threading.Thread): shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') shared.sqlLock.release() - #self.emit(SIGNAL("displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.inventoryHash,toAddress,fromAddress,subject,body) 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. @@ -834,7 +827,6 @@ class receiveDataThread(threading.Thread): shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') shared.sqlLock.release() - #self.emit(SIGNAL("displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.inventoryHash,toAddress,fromAddress,subject,body) 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. @@ -896,7 +888,6 @@ class receiveDataThread(threading.Thread): shared.inventory[self.inventoryHash] = (objectType, self.streamNumber, data, embeddedTime) shared.inventoryLock.release() self.broadcastinv(self.inventoryHash) - #self.emit(SIGNAL("incrementNumberOfMessagesProcessed()")) shared.UISignalQueue.put(('incrementNumberOfMessagesProcessed','no data')) @@ -1116,7 +1107,6 @@ class receiveDataThread(threading.Thread): shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') shared.sqlLock.release() - #self.emit(SIGNAL("displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.inventoryHash,toAddress,fromAddress,subject,body) 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. @@ -1150,7 +1140,6 @@ class receiveDataThread(threading.Thread): shared.sqlSubmitQueue.put('commit') shared.sqlLock.release() - #self.emit(SIGNAL("displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),toAddress,'[Broadcast subscribers]',fromAddress,subject,message,ackdata) shared.UISignalQueue.put(('displayNewSentMessage',(toAddress,'[Broadcast subscribers]',fromAddress,subject,message,ackdata))) shared.workerQueue.put(('sendbroadcast','')) @@ -1244,7 +1233,6 @@ class receiveDataThread(threading.Thread): shared.inventory[inventoryHash] = (objectType, self.streamNumber, data, embeddedTime) shared.inventoryLock.release() self.broadcastinv(inventoryHash) - #self.emit(SIGNAL("incrementNumberOfPubkeysProcessed()")) shared.UISignalQueue.put(('incrementNumberOfPubkeysProcessed','no data')) self.processpubkey(data) @@ -1888,10 +1876,7 @@ class receiveDataThread(threading.Thread): PORT, timeLastReceivedMessageFromThisNode = value if timeLastReceivedMessageFromThisNode > (int(time.time())- maximumAgeOfNodesThatIAdvertiseToOthers): #If it is younger than 3 hours old.. numberOfAddressesInAddrMessage += 1 - if self.remoteProtocolVersion == 1: - payload += pack('>I',timeLastReceivedMessageFromThisNode) #32-bit time - else: - payload += pack('>Q',timeLastReceivedMessageFromThisNode) #64-bit time + payload += pack('>Q',timeLastReceivedMessageFromThisNode) #64-bit time payload += pack('>I',self.streamNumber) payload += pack('>q',1) #service bit flags offered by this node payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + socket.inet_aton(HOST) @@ -1900,10 +1885,7 @@ class receiveDataThread(threading.Thread): PORT, timeLastReceivedMessageFromThisNode = value if timeLastReceivedMessageFromThisNode > (int(time.time())- maximumAgeOfNodesThatIAdvertiseToOthers): #If it is younger than 3 hours old.. numberOfAddressesInAddrMessage += 1 - if self.remoteProtocolVersion == 1: - payload += pack('>I',timeLastReceivedMessageFromThisNode) #32-bit time - else: - payload += pack('>Q',timeLastReceivedMessageFromThisNode) #64-bit time + payload += pack('>Q',timeLastReceivedMessageFromThisNode) #64-bit time payload += pack('>I',self.streamNumber*2) payload += pack('>q',1) #service bit flags offered by this node payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + socket.inet_aton(HOST) @@ -1912,10 +1894,7 @@ class receiveDataThread(threading.Thread): PORT, timeLastReceivedMessageFromThisNode = value if timeLastReceivedMessageFromThisNode > (int(time.time())- maximumAgeOfNodesThatIAdvertiseToOthers): #If it is younger than 3 hours old.. numberOfAddressesInAddrMessage += 1 - if self.remoteProtocolVersion == 1: - payload += pack('>I',timeLastReceivedMessageFromThisNode) #32-bit time - else: - payload += pack('>Q',timeLastReceivedMessageFromThisNode) #64-bit time + payload += pack('>Q',timeLastReceivedMessageFromThisNode) #64-bit time payload += pack('>I',(self.streamNumber*2)+1) payload += pack('>q',1) #service bit flags offered by this node payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + socket.inet_aton(HOST) @@ -1945,6 +1924,12 @@ class receiveDataThread(threading.Thread): return elif not self.verackSent: self.remoteProtocolVersion, = unpack('>L',data[:4]) + if self.remoteProtocolVersion <= 1: + shared.broadcastToSendDataQueues((0, 'shutdown', self.HOST)) + shared.printLock.acquire() + print 'Closing connection to old protocol version 1 node: ', self.HOST + shared.printLock.release() + return #print 'remoteProtocolVersion', self.remoteProtocolVersion self.myExternalIP = socket.inet_ntoa(data[40:44]) #print 'myExternalIP', self.myExternalIP @@ -2098,27 +2083,22 @@ class sendDataThread(threading.Thread): shared.printLock.release() self.remoteProtocolVersion = specifiedRemoteProtocolVersion elif command == 'sendaddr': - if self.remoteProtocolVersion == 1: - shared.printLock.acquire() - print 'a sendData thread is not sending an addr message to this particular peer ('+self.HOST+') because their protocol version is 1.' - shared.printLock.release() - else: + try: + #To prevent some network analysis, 'leak' the data out to our peer after waiting a random amount of time unless we have a long list of messages in our queue to send. + random.seed() + time.sleep(random.randrange(0, 10)) + self.sock.sendall(data) + self.lastTimeISentData = int(time.time()) + except: + print 'self.sock.sendall failed' try: - #To prevent some network analysis, 'leak' the data out to our peer after waiting a random amount of time unless we have a long list of messages in our queue to send. - random.seed() - time.sleep(random.randrange(0, 10)) - self.sock.sendall(data) - self.lastTimeISentData = int(time.time()) + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() except: - print 'self.sock.sendall failed' - try: - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() - except: - pass - shared.sendDataQueues.remove(self.mailbox) - print 'sendDataThread thread (ID:',str(id(self))+') ending now. Was connected to', self.HOST - break + pass + shared.sendDataQueues.remove(self.mailbox) + print 'sendDataThread thread (ID:',str(id(self))+') ending now. Was connected to', self.HOST + break elif command == 'sendinv': if data not in self.objectsOfWhichThisRemoteNodeIsAlreadyAware: payload = '\x01' + data @@ -2498,6 +2478,11 @@ class sqlThread(threading.Thread): self.conn = sqlite3.connect(shared.appdata + 'messages.dat' ) self.conn.text_factory = str self.cur = self.conn.cursor() + elif item == 'deleteandvacuume': + self.cur.execute('''delete from inbox where folder='trash' ''') + self.cur.execute('''delete from sent where folder='trash' ''') + self.conn.commit() + self.cur.execute( ''' VACUUM ''') else: parameters = shared.sqlSubmitQueue.get() #print 'item', item @@ -2538,7 +2523,6 @@ class singleCleaner(threading.Thread): while True: shared.sqlLock.acquire() - #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"Doing housekeeping (Flushing inventory in memory to disk...)") shared.UISignalQueue.put(('updateStatusBar','Doing housekeeping (Flushing inventory in memory to disk...)')) for hash, storedValue in shared.inventory.items(): objectType, streamNumber, payload, receivedTime = storedValue @@ -2592,7 +2576,6 @@ class singleCleaner(threading.Thread): except: pass - #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"Doing work necessary to again attempt to request a public key...") shared.UISignalQueue.put(('updateStatusBar','Doing work necessary to again attempt to request a public key...')) t = (int(time.time()),pubkeyretrynumber+1,toripe) shared.sqlSubmitQueue.put('''UPDATE sent SET lastactiontime=?, pubkeyretrynumber=?, status='msgqueued' WHERE toripe=?''') @@ -2609,7 +2592,6 @@ class singleCleaner(threading.Thread): shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') shared.workerQueue.put(('sendmessage','')) - #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"Doing work necessary to again attempt to deliver a message...") shared.UISignalQueue.put(('updateStatusBar','Doing work necessary to again attempt to deliver a message...')) shared.sqlSubmitQueue.put('commit') shared.sqlLock.release() @@ -2746,7 +2728,6 @@ class singleWorker(threading.Thread): print 'broadcasting inv with hash:', inventoryHash.encode('hex') shared.printLock.release() shared.broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash)) - #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"") shared.UISignalQueue.put(('updateStatusBar','')) shared.config.set(myAddress,'lastpubkeysendtime',str(int(time.time()))) with open(shared.appdata + 'keys.dat', 'wb') as configfile: @@ -2808,7 +2789,6 @@ class singleWorker(threading.Thread): print 'broadcasting inv with hash:', inventoryHash.encode('hex') shared.printLock.release() shared.broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash)) - #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),"") shared.UISignalQueue.put(('updateStatusBar','')) shared.config.set(myAddress,'lastpubkeysendtime',str(int(time.time()))) with open(shared.appdata + 'keys.dat', 'wb') as configfile: @@ -2830,7 +2810,6 @@ class singleWorker(threading.Thread): privSigningKeyBase58 = shared.config.get(fromaddress, 'privsigningkey') privEncryptionKeyBase58 = shared.config.get(fromaddress, 'privencryptionkey') except: - #self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Error! Could not find sender address (your address) in the keys.dat file.') shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Error! Could not find sender address (your address) in the keys.dat file.'))) continue @@ -2840,7 +2819,7 @@ class singleWorker(threading.Thread): pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode('hex') #At this time these pubkeys are 65 bytes long because they include the encoding byte which we won't be sending in the broadcast message. pubEncryptionKey = highlevelcrypto.privToPub(privEncryptionKeyHex).decode('hex') - payload = pack('>I',(int(time.time())+random.randrange(-300, 300)))#the current time plus or minus five minutes + payload = pack('>Q',(int(time.time())+random.randrange(-300, 300)))#the current time plus or minus five minutes payload += encodeVarint(1) #broadcast version payload += encodeVarint(addressVersionNumber) payload += encodeVarint(streamNumber) @@ -2858,7 +2837,6 @@ class singleWorker(threading.Thread): target = 2**64 / ((len(payload)+shared.networkDefaultPayloadLengthExtraBytes+8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) print '(For broadcast message) Doing proof of work...' - #self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Doing work necessary to send broadcast...') shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send broadcast...'))) initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) @@ -2872,7 +2850,6 @@ class singleWorker(threading.Thread): print 'Broadcasting inv for my broadcast (within sendBroadcast function):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash)) - #self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Broadcast sent on '+unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')) shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Broadcast sent on '+unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))) #Update the status of the message in the 'sent' table to have a 'broadcastsent' status @@ -2889,7 +2866,6 @@ class singleWorker(threading.Thread): privSigningKeyBase58 = shared.config.get(fromaddress, 'privsigningkey') privEncryptionKeyBase58 = shared.config.get(fromaddress, 'privencryptionkey') except: - #self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Error! Could not find sender address (your address) in the keys.dat file.') shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Error! Could not find sender address (your address) in the keys.dat file.'))) continue @@ -2899,7 +2875,7 @@ class singleWorker(threading.Thread): pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode('hex') #At this time these pubkeys are 65 bytes long because they include the encoding byte which we won't be sending in the broadcast message. pubEncryptionKey = highlevelcrypto.privToPub(privEncryptionKeyHex).decode('hex') - payload = pack('>I',(int(time.time())+random.randrange(-300, 300)))#the current time plus or minus five minutes + payload = pack('>Q',(int(time.time())+random.randrange(-300, 300)))#the current time plus or minus five minutes payload += encodeVarint(2) #broadcast version payload += encodeVarint(streamNumber) @@ -2924,7 +2900,6 @@ class singleWorker(threading.Thread): target = 2**64 / ((len(payload)+shared.networkDefaultPayloadLengthExtraBytes+8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) print '(For broadcast message) Doing proof of work...' - #self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Doing work necessary to send broadcast...') shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send broadcast...'))) initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) @@ -2938,7 +2913,6 @@ class singleWorker(threading.Thread): print 'sending inv (within sendBroadcast function)' shared.broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash)) - #self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Broadcast sent on '+unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')) shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Broadcast sent on '+unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))) #Update the status of the message in the 'sent' table to have a 'broadcastsent' status @@ -3000,12 +2974,12 @@ class singleWorker(threading.Thread): shared.UISignalQueue.put(('updateSentItemStatusByHash',(toripe,'Sending a request for the recipient\'s encryption key.'))) self.requestPubKey(toaddress) shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT toaddress, toripe, fromaddress, subject, message, ackdata FROM sent WHERE status='doingmsgpow' and folder='sent' ''') - shared.sqlSubmitQueue.put('') + shared.sqlSubmitQueue.put('''SELECT toaddress, toripe, fromaddress, subject, message, ackdata, status FROM sent WHERE (status='doingmsgpow' or status='forcepow' or (status='toodifficult' and lastactiontime>?)) and folder='sent' ''') + shared.sqlSubmitQueue.put((int(time.time())-2419200,)) queryreturn = shared.sqlReturnQueue.get() shared.sqlLock.release() for row in queryreturn: - toaddress, toripe, fromaddress, subject, message, ackdata = row + toaddress, toripe, fromaddress, subject, message, ackdata, status = row #Evidently there is a remote possibility that we may no longer have the recipient's pubkey. Let us make sure we still have it or else the sendMsg function will appear to freeze. shared.sqlLock.acquire() @@ -3036,7 +3010,72 @@ class singleWorker(threading.Thread): print 'Found a message in our database that needs to be sent with this pubkey.' print 'First 150 characters of message:', repr(message[:150]) shared.printLock.release() - embeddedTime = pack('>I',(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. + + #Now let us fetch the recipient's public key out of our database. If the required proof of work difficulty is too hard then we'll abort. + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('SELECT transmitdata FROM pubkeys WHERE hash=?') + shared.sqlSubmitQueue.put((toripe,)) + queryreturn = shared.sqlReturnQueue.get() + #also mark the pubkey as 'usedpersonally' so that we don't delete it. + t = (toripe,) + shared.sqlSubmitQueue.put('''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=?''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + if queryreturn == []: + shared.printLock.acquire() + sys.stderr.write('(within sendMsg) The needed pubkey was not found. This should never happen. Aborting send.\n') + shared.printLock.release() + return + for row in queryreturn: + pubkeyPayload, = row + + #The pubkey is stored the way we originally received it which means that we need to read beyond things like the nonce and time to get to the public keys. + readPosition = 8 #to bypass the nonce + pubkeyEmbeddedTime, = unpack('>I',pubkeyPayload[readPosition:readPosition+4]) + #This section is used for the transition from 32 bit time to 64 bit time in the protocol. + if pubkeyEmbeddedTime == 0: + pubkeyEmbeddedTime, = unpack('>Q',pubkeyPayload[readPosition:readPosition+8]) + readPosition += 8 + else: + readPosition += 4 + readPosition += 1 #to bypass the address version whose length is definitely 1 + streamNumber, streamNumberLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) + readPosition += streamNumberLength + behaviorBitfield = pubkeyPayload[readPosition:readPosition+4] + readPosition += 4 #to bypass the bitfield of behaviors + #pubSigningKeyBase256 = pubkeyPayload[readPosition:readPosition+64] #We don't use this key for anything here. + readPosition += 64 + pubEncryptionKeyBase256 = pubkeyPayload[readPosition:readPosition+64] + readPosition += 64 + if toAddressVersionNumber == 2: + requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte + requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes + elif toAddressVersionNumber == 3: + requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) + readPosition += varintLength + requiredPayloadLengthExtraBytes, varintLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) + readPosition += varintLength + if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte: #We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network. + requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte + if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes: + requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes + if status != 'forcepow': + if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') != 0): + #The demanded difficulty is more than we are willing to do. + shared.sqlLock.acquire() + t = (ackdata,) + shared.sqlSubmitQueue.put('''UPDATE sent SET status='toodifficult' WHERE ackdata=? AND status='doingmsgpow' ''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Problem: The work demanded by the recipient (' + str(float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte) + ' and ' + str(float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes) + ') is more difficult than you are willing to do. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))) + continue + + + 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. if fromAddressVersionNumber == 2: payload = '\x01' #Message version. payload += encodeVarint(fromAddressVersionNumber) @@ -3048,7 +3087,6 @@ class singleWorker(threading.Thread): privSigningKeyBase58 = shared.config.get(fromaddress, 'privsigningkey') privEncryptionKeyBase58 = shared.config.get(fromaddress, 'privencryptionkey') except: - #self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Error! Could not find sender address (your address) in the keys.dat file.') shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Error! Could not find sender address (your address) in the keys.dat file.'))) continue @@ -3084,7 +3122,6 @@ class singleWorker(threading.Thread): privSigningKeyBase58 = shared.config.get(fromaddress, 'privsigningkey') privEncryptionKeyBase58 = shared.config.get(fromaddress, 'privencryptionkey') except: - #self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Error! Could not find sender address (your address) in the keys.dat file.') shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Error! Could not find sender address (your address) in the keys.dat file.'))) continue @@ -3116,71 +3153,9 @@ class singleWorker(threading.Thread): payload += encodeVarint(len(signature)) payload += signature - - #We have assembled the data that will be encrypted. Now let us fetch the recipient's public key out of our database and do the encryption. - - if toAddressVersionNumber == 2 or toAddressVersionNumber == 3: - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('SELECT transmitdata FROM pubkeys WHERE hash=?') - shared.sqlSubmitQueue.put((toripe,)) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() - if queryreturn == []: - shared.printLock.acquire() - sys.stderr.write('(within sendMsg) The needed pubkey was not found. This should never happen. Aborting send.\n') - shared.printLock.release() - return - for row in queryreturn: - pubkeyPayload, = row - - #The pubkey is stored the way we originally received it which means that we need to read beyond things like the nonce and time to get to the public keys. - readPosition = 8 #to bypass the nonce - - pubkeyEmbeddedTime, = unpack('>I',pubkeyPayload[readPosition:readPosition+4]) - #This section is used for the transition from 32 bit time to 64 bit time in the protocol. - if pubkeyEmbeddedTime == 0: - pubkeyEmbeddedTime, = unpack('>Q',pubkeyPayload[readPosition:readPosition+8]) - readPosition += 8 - else: - readPosition += 4 - - readPosition += 1 #to bypass the address version whose length is definitely 1 - streamNumber, streamNumberLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) - readPosition += streamNumberLength - behaviorBitfield = pubkeyPayload[readPosition:readPosition+4] - readPosition += 4 #to bypass the bitfield of behaviors - #pubSigningKeyBase256 = pubkeyPayload[readPosition:readPosition+64] #We don't use this key for anything here. - readPosition += 64 - pubEncryptionKeyBase256 = pubkeyPayload[readPosition:readPosition+64] - readPosition += 64 - if toAddressVersionNumber == 2: - requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte - requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes - elif toAddressVersionNumber == 3: - requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) - readPosition += varintLength - requiredPayloadLengthExtraBytes, varintLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) - readPosition += varintLength - if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte: #We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network. - requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte - if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes: - requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes - if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') != 0): - #The demanded difficulty is more than we are willing to do. - shared.sqlLock.acquire() - t = (ackdata,) - shared.sqlSubmitQueue.put('''UPDATE sent SET status='toodifficult' WHERE ackdata=? AND status='doingmsgpow' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Problem: The work demanded by the recipient (' + str(requiredAverageProofOfWorkNonceTrialsPerByte / shared.networkDefaultProofOfWorkNonceTrialsPerByte) + ' and ' + str(requiredPayloadLengthExtraBytes / shared.networkDefaultPayloadLengthExtraBytes) + ') is more difficult than you are willing to do. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))) - continue - - - encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex')) - - #We are now dropping the unencrypted data in payload since it has already been encrypted and replacing it with the encrypted payload that we will send out. + #We have assembled the data that will be encrypted. + encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex')) + #We will now drop the unencrypted data in payload since it has already been encrypted and replace it with the encrypted payload that we will send out. payload = embeddedTime + encodeVarint(toStreamNumber) + encrypted target = 2**64 / ((len(payload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte) shared.printLock.acquire() @@ -3199,20 +3174,14 @@ class singleWorker(threading.Thread): inventoryHash = calculateInventoryHash(payload) objectType = 'msg' shared.inventory[inventoryHash] = (objectType, toStreamNumber, payload, int(time.time())) - #self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Message sent. Waiting on acknowledgement. Sent on ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')) shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Message sent. Waiting on acknowledgement. Sent on ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))) print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash)) #Update the status of the message in the 'sent' table to have a 'msgsent' status shared.sqlLock.acquire() - t = ('msgsent',toaddress, fromaddress, subject, message,'doingmsgpow') - shared.sqlSubmitQueue.put('UPDATE sent SET status=? WHERE toaddress=? AND fromaddress=? AND subject=? AND message=? AND status=?') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - - t = (toripe,) - shared.sqlSubmitQueue.put('''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=?''') + t = (toaddress, fromaddress, subject, message) + shared.sqlSubmitQueue.put('''UPDATE sent SET status='msgsent' WHERE toaddress=? AND fromaddress=? AND subject=? AND message=? AND status='doingmsgpow' or status='forcepow' ''') shared.sqlSubmitQueue.put(t) queryreturn = shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') @@ -3227,7 +3196,7 @@ class singleWorker(threading.Thread): shared.printLock.release() return neededPubkeys[ripe] = 0 - payload = pack('>I',(int(time.time())+random.randrange(-300, 300)))#the current time plus or minus five minutes. + payload = pack('>Q',(int(time.time())+random.randrange(-300, 300)))#the current time plus or minus five minutes. payload += encodeVarint(addressVersionNumber) payload += encodeVarint(streamNumber) payload += ripe @@ -3238,7 +3207,6 @@ class singleWorker(threading.Thread): statusbar = 'Doing the computations necessary to request the recipient\'s public key.' shared.UISignalQueue.put(('updateStatusBar',statusbar)) shared.UISignalQueue.put(('updateSentItemStatusByHash',(ripe,'Doing work necessary to request encryption key.'))) - print 'Doing proof-of-work necessary to send getpubkey message.' target = 2**64 / ((len(payload)+shared.networkDefaultPayloadLengthExtraBytes+8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) @@ -3370,9 +3338,7 @@ class addressGenerator(threading.Thread): #It may be the case that this address is being generated as a result of a call to the API. Let us put the result in the necessary queue. apiAddressGeneratorReturnQueue.put(address) - #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Done generating address. Doing work necessary to broadcast it...') shared.UISignalQueue.put(('updateStatusBar','Done generating address. Doing work necessary to broadcast it...')) - #self.emit(SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.label,address,str(streamNumber)) shared.UISignalQueue.put(('writeNewAddressToTable',(label,address,streamNumber))) shared.reloadMyAddressHashes() shared.workerQueue.put(('doPOWForMyV3Pubkey',ripe.digest())) @@ -3443,20 +3409,22 @@ class addressGenerator(threading.Thread): with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) - #self.emit(SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.label,address,str(self.streamNumber)) shared.UISignalQueue.put(('writeNewAddressToTable',(label,address,str(streamNumber)))) listOfNewAddressesToSendOutThroughTheAPI.append(address) - if eighteenByteRipe: - shared.reloadMyAddressHashes()#This is necessary here (rather than just at the end) because otherwise if the human generates a large number of new addresses and uses one before they are done generating, the program will receive a getpubkey message and will ignore it. + #if eighteenByteRipe: + # shared.reloadMyAddressHashes()#This is necessary here (rather than just at the end) because otherwise if the human generates a large number of new addresses and uses one before they are done generating, the program will receive a getpubkey message and will ignore it. + shared.myECCryptorObjects[ripe.digest()] = highlevelcrypto.makeCryptor(potentialPrivEncryptionKey.encode('hex')) + shared.myAddressesByHash[ripe.digest()] = address + shared.workerQueue.put(('doPOWForMyV3Pubkey',ripe.digest())) except: print address,'already exists. Not adding it again.' #Done generating addresses. - #It may be the case that this address is being generated as a result of a call to the API. Let us put the result in the necessary queue. if command == 'createDeterministicAddresses': + #It may be the case that this address is being generated as a result of a call to the API. Let us put the result in the necessary queue. apiAddressGeneratorReturnQueue.put(listOfNewAddressesToSendOutThroughTheAPI) shared.UISignalQueue.put(('updateStatusBar','Done generating address')) - shared.reloadMyAddressHashes() + #shared.reloadMyAddressHashes() elif command == 'getDeterministicAddress': apiAddressGeneratorReturnQueue.put(address) else: @@ -3593,6 +3561,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): else: return 'API Error 0000: Too many parameters!' label = label.decode('base64') + try: + unicode(label,'utf-8') + except: + return 'API Error 0017: Label is not valid UTF-8 data.' apiAddressGeneratorReturnQueue.queue.clear() streamNumberForAddress = 1 shared.addressGeneratorQueue.put(('createRandomAddress',3,streamNumberForAddress,label,1,"",eighteenByteRipe,nonceTrialsPerByte,payloadLengthExtraBytes)) @@ -3692,6 +3664,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data = '{"inboxMessages":[' for row in queryreturn: msgid, toAddress, fromAddress, subject, received, message, = 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':2,'receivedTime':received},indent=4, separators=(',', ': ')) @@ -3836,8 +3810,6 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.sqlLock.release() toLabel = '[Broadcast subscribers]' - #apiSignalQueue.put(('displayNewSentMessage',(toAddress,toLabel,fromAddress,subject,message,ackdata))) - #self.emit(SIGNAL("displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),toAddress,toLabel,fromAddress,subject,message,ackdata) shared.UISignalQueue.put(('displayNewSentMessage',(toAddress,toLabel,fromAddress,subject,message,ackdata))) shared.workerQueue.put(('sendbroadcast','')) @@ -3868,8 +3840,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): address, label = params label = label.decode('base64') try: - label.decode('utf-8') - except UnicodeDecodeError: + unicode(label,'utf-8') + except: return 'API Error 0017: Label is not valid UTF-8 data.' if len(params) >2: return 'API Error 0000: I need either 1 or 2 parameters!' diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 94a525fb..356d3e2e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -169,11 +169,12 @@ class MyForm(QtGui.QMainWindow): # Actions self.actionTrashSentMessage = self.ui.sentContextMenuToolbar.addAction("Move to Trash", self.on_action_SentTrash) self.actionSentClipboard = self.ui.sentContextMenuToolbar.addAction("Copy destination address to clipboard", self.on_action_SentClipboard) + self.actionForceSend = self.ui.sentContextMenuToolbar.addAction("Force send", self.on_action_ForceSend) self.ui.tableWidgetSent.setContextMenuPolicy( QtCore.Qt.CustomContextMenu ) self.connect(self.ui.tableWidgetSent, QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), self.on_context_menuSent) - self.popMenuSent = QtGui.QMenu( self ) - self.popMenuSent.addAction( self.actionSentClipboard ) - self.popMenuSent.addAction( self.actionTrashSentMessage ) + #self.popMenuSent = QtGui.QMenu( self ) + #self.popMenuSent.addAction( self.actionSentClipboard ) + #self.popMenuSent.addAction( self.actionTrashSentMessage ) #Popup menu for the Blacklist page @@ -220,10 +221,9 @@ class MyForm(QtGui.QMainWindow): if isEnabled: status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) - self.ui.tableWidgetSent.keyPressEvent = self.tableWidgetSentKeyPressEvent + #Load inbox from messages database file font = QFont() font.setBold(True) - #Load inbox from messages database file shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, received, message, read FROM inbox where folder='inbox' ORDER BY received''') shared.sqlSubmitQueue.put('') @@ -231,7 +231,8 @@ class MyForm(QtGui.QMainWindow): shared.sqlLock.release() for row in queryreturn: msgid, toAddress, fromAddress, subject, received, message, read = row - + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) try: if toAddress == self.str_broadcast_subscribers: toLabel = self.str_broadcast_subscribers @@ -268,6 +269,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(toLabel,'utf-8')) + newItem.setToolTip(unicode(toLabel,'utf-8')) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) if not read: newItem.setFont(font) @@ -277,8 +279,10 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.setItem(0,0,newItem) if fromLabel == '': newItem = QtGui.QTableWidgetItem(unicode(fromAddress,'utf-8')) + newItem.setToolTip(unicode(fromAddress,'utf-8')) else: newItem = QtGui.QTableWidgetItem(unicode(fromLabel,'utf-8')) + newItem.setToolTip(unicode(fromLabel,'utf-8')) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) if not read: newItem.setFont(font) @@ -286,12 +290,14 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.setItem(0,1,newItem) newItem = QtGui.QTableWidgetItem(unicode(subject,'utf-8')) + newItem.setToolTip(unicode(subject,'utf-8')) newItem.setData(Qt.UserRole,unicode(message,'utf-8)')) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) if not read: newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0,2,newItem) newItem = myTableWidgetItem(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(received))),'utf-8')) + newItem.setToolTip(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(received))),'utf-8')) newItem.setData(Qt.UserRole,QByteArray(msgid)) newItem.setData(33,int(received)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) @@ -299,8 +305,8 @@ class MyForm(QtGui.QMainWindow): newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0,3,newItem) self.ui.tableWidgetInbox.sortItems(3,Qt.DescendingOrder) - self.ui.tableWidgetInbox.keyPressEvent = self.tableWidgetInboxKeyPressEvent + #Load Sent items from database shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''SELECT toaddress, fromaddress, subject, message, status, ackdata, lastactiontime FROM sent where folder = 'sent' ORDER BY lastactiontime''') @@ -309,6 +315,8 @@ class MyForm(QtGui.QMainWindow): shared.sqlLock.release() for row in queryreturn: toAddress, fromAddress, subject, message, status, ackdata, lastactiontime = row + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) try: fromLabel = shared.config.get(fromAddress, 'label') except: @@ -331,47 +339,57 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetSent.insertRow(0) if toLabel == '': newItem = QtGui.QTableWidgetItem(unicode(toAddress,'utf-8')) + newItem.setToolTip(unicode(toAddress,'utf-8')) else: newItem = QtGui.QTableWidgetItem(unicode(toLabel,'utf-8')) + newItem.setToolTip(unicode(toLabel,'utf-8')) newItem.setData(Qt.UserRole,str(toAddress)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) self.ui.tableWidgetSent.setItem(0,0,newItem) if fromLabel == '': newItem = QtGui.QTableWidgetItem(unicode(fromAddress,'utf-8')) + newItem.setToolTip(unicode(fromAddress,'utf-8')) else: newItem = QtGui.QTableWidgetItem(unicode(fromLabel,'utf-8')) + newItem.setToolTip(unicode(fromLabel,'utf-8')) newItem.setData(Qt.UserRole,str(fromAddress)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) self.ui.tableWidgetSent.setItem(0,1,newItem) newItem = QtGui.QTableWidgetItem(unicode(subject,'utf-8')) + newItem.setToolTip(unicode(subject,'utf-8')) newItem.setData(Qt.UserRole,unicode(message,'utf-8)')) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) self.ui.tableWidgetSent.setItem(0,2,newItem) if status == 'awaitingpubkey': - newItem = myTableWidgetItem('Waiting on their encryption key. Will request it again soon.') + statusText = 'Waiting on their encryption key. Will request it again soon.' elif status == 'doingpowforpubkey': - newItem = myTableWidgetItem('Encryption key request queued.') + statusText = 'Encryption key request queued.' elif status == 'msgqueued': - newItem = myTableWidgetItem('Queued.') + statusText = 'Queued.' elif status == 'msgsent': - newItem = myTableWidgetItem('Message sent. Waiting on acknowledgement. Sent at ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(lastactiontime)),'utf-8')) + statusText = 'Message sent. Waiting on acknowledgement. Sent at ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(lastactiontime)),'utf-8') elif status == 'doingmsgpow': - newItem = myTableWidgetItem('Need to do work to send message. Work is queued.') + statusText = 'Need to do work to send message. Work is queued.' elif status == 'ackreceived': - newItem = myTableWidgetItem('Acknowledgement of the message received ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) + statusText = 'Acknowledgement of the message received ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8') elif status == 'broadcastqueued': - newItem = myTableWidgetItem('Broadcast queued.') + statusText = 'Broadcast queued.' elif status == 'broadcastsent': - newItem = myTableWidgetItem('Broadcast on ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) + statusText = 'Broadcast on ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8') elif status == 'toodifficult': - newItem = myTableWidgetItem('Problem: The work demanded by the recipient is more difficult than you are willing to do. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) + statusText = 'Problem: The work demanded by the recipient is more difficult than you are willing to do. ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8') + elif status == 'forcepow': + statusText = 'Forced difficulty override. Send should start soon.' else: - newItem = myTableWidgetItem('Unknown status: ' + status + ' ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8')) + statusText = 'Unknown status: ' + status + ' ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(lastactiontime))),'utf-8') + newItem = myTableWidgetItem(statusText) + newItem.setToolTip(statusText) newItem.setData(Qt.UserRole,QByteArray(ackdata)) newItem.setData(33,int(lastactiontime)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) self.ui.tableWidgetSent.setItem(0,3,newItem) self.ui.tableWidgetSent.sortItems(3,Qt.DescendingOrder) + self.ui.tableWidgetSent.keyPressEvent = self.tableWidgetSentKeyPressEvent #Initialize the address book shared.sqlLock.acquire() @@ -766,19 +784,8 @@ class MyForm(QtGui.QMainWindow): def click_actionDeleteAllTrashedMessages(self): if QtGui.QMessageBox.question(self, 'Delete trash?',"Are you sure you want to delete all trashed messages?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) == QtGui.QMessageBox.No: return - self.statusBar().showMessage('Deleting messages and freeing empty space...') shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''delete from inbox where folder='trash' ''') - shared.sqlSubmitQueue.put('') - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('''delete from sent where folder='trash' ''') - shared.sqlSubmitQueue.put('') - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') #Commit takes no parameters and returns nothing - shared.sqlSubmitQueue.put('vacuum') - shared.sqlSubmitQueue.put('') - shared.sqlReturnQueue.get() - self.statusBar().showMessage('') + shared.sqlSubmitQueue.put('deleteandvacuume') shared.sqlLock.release() def click_actionRegenerateDeterministicAddresses(self): @@ -928,8 +935,8 @@ class MyForm(QtGui.QMainWindow): toAddress = str(self.ui.tableWidgetSent.item(i,0).data(Qt.UserRole).toPyObject()) status,addressVersionNumber,streamNumber,ripe = decodeAddress(toAddress) if ripe == toRipe: - #self.ui.tableWidgetSent.item(i,3).setText(unicode(textToDisplay,'utf-8')) self.ui.tableWidgetSent.item(i,3).setText(textToDisplay) + self.ui.tableWidgetSent.item(i,3).setToolTip(textToDisplay) def updateSentItemStatusByAckdata(self,ackdata,textToDisplay): for i in range(self.ui.tableWidgetSent.rowCount()): @@ -939,6 +946,7 @@ class MyForm(QtGui.QMainWindow): if ackdata == tableAckdata: #self.ui.tableWidgetSent.item(i,3).setText(unicode(textToDisplay,'utf-8')) self.ui.tableWidgetSent.item(i,3).setText(textToDisplay) + self.ui.tableWidgetSent.item(i,3).setToolTip(textToDisplay) def removeInboxRowByMsgid(self,msgid):#msgid and inventoryHash are the same thing for i in range(self.ui.tableWidgetInbox.rowCount()): @@ -1080,7 +1088,7 @@ class MyForm(QtGui.QMainWindow): continue except: pass - if addressVersionNumber > 3 or addressVersionNumber == 0: + if addressVersionNumber > 3 or addressVersionNumber <= 1: QMessageBox.about(self, "Address version number", "Concerning the address "+toAddress+", Bitmessage cannot understand address version numbers of "+str(addressVersionNumber)+". Perhaps upgrade Bitmessage to the latest version.") continue if streamNumber > 1 or streamNumber == 0: @@ -1210,6 +1218,8 @@ class MyForm(QtGui.QMainWindow): #This function is called by the processmsg function when that function receives a message to an address that is acting as a pseudo-mailing-list. The message will be broadcast out. This function puts the message on the 'Sent' tab. def displayNewSentMessage(self,toAddress,toLabel,fromAddress,subject,message,ackdata): + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) try: fromLabel = shared.config.get(fromAddress, 'label') except: @@ -1221,21 +1231,27 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetSent.insertRow(0) if toLabel == '': newItem = QtGui.QTableWidgetItem(unicode(toAddress,'utf-8')) + newItem.setToolTip(unicode(toAddress,'utf-8')) else: newItem = QtGui.QTableWidgetItem(unicode(toLabel,'utf-8')) + newItem.setToolTip(unicode(toLabel,'utf-8')) newItem.setData(Qt.UserRole,str(toAddress)) self.ui.tableWidgetSent.setItem(0,0,newItem) if fromLabel == '': newItem = QtGui.QTableWidgetItem(unicode(fromAddress,'utf-8')) + newItem.setToolTip(unicode(fromAddress,'utf-8')) else: newItem = QtGui.QTableWidgetItem(unicode(fromLabel,'utf-8')) + newItem.setToolTip(unicode(fromLabel,'utf-8')) newItem.setData(Qt.UserRole,str(fromAddress)) self.ui.tableWidgetSent.setItem(0,1,newItem) newItem = QtGui.QTableWidgetItem(unicode(subject,'utf-8)')) + newItem.setToolTip(unicode(subject,'utf-8)')) newItem.setData(Qt.UserRole,unicode(message,'utf-8)')) self.ui.tableWidgetSent.setItem(0,2,newItem) #newItem = QtGui.QTableWidgetItem('Doing work necessary to send broadcast...'+ unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')) newItem = myTableWidgetItem('Work is queued. '+ unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')) + newItem.setToolTip('Work is queued. '+ unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')) newItem.setData(Qt.UserRole,QByteArray(ackdata)) newItem.setData(33,int(time.time())) self.ui.tableWidgetSent.setItem(0,3,newItem) @@ -1243,6 +1259,8 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetSent.setSortingEnabled(True) def displayNewInboxMessage(self,inventoryHash,toAddress,fromAddress,subject,message): + subject = shared.fixPotentiallyInvalidUTF8Data(subject) + message = shared.fixPotentiallyInvalidUTF8Data(message) fromLabel = '' shared.sqlLock.acquire() t = (fromAddress,) @@ -1279,6 +1297,7 @@ class MyForm(QtGui.QMainWindow): font.setBold(True) self.ui.tableWidgetInbox.setSortingEnabled(False) newItem = QtGui.QTableWidgetItem(unicode(toLabel,'utf-8')) + newItem.setToolTip(unicode(toLabel,'utf-8')) newItem.setFont(font) newItem.setData(Qt.UserRole,str(toAddress)) if shared.safeConfigGetBoolean(str(toAddress),'mailinglist'): @@ -1288,20 +1307,24 @@ class MyForm(QtGui.QMainWindow): if fromLabel == '': newItem = QtGui.QTableWidgetItem(unicode(fromAddress,'utf-8')) + newItem.setToolTip(unicode(fromAddress,'utf-8')) if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): self.notifierShow('New Message', 'From '+ fromAddress) else: newItem = QtGui.QTableWidgetItem(unicode(fromLabel,'utf-8')) + newItem.setToolTip(unicode(unicode(fromLabel,'utf-8'))) if shared.config.getboolean('bitmessagesettings', 'showtraynotifications'): self.notifierShow('New Message', 'From ' + fromLabel) newItem.setData(Qt.UserRole,str(fromAddress)) newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0,1,newItem) newItem = QtGui.QTableWidgetItem(unicode(subject,'utf-8)')) + newItem.setToolTip(unicode(subject,'utf-8)')) newItem.setData(Qt.UserRole,unicode(message,'utf-8)')) newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0,2,newItem) newItem = myTableWidgetItem(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')) + newItem.setToolTip(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')) newItem.setData(Qt.UserRole,QByteArray(inventoryHash)) newItem.setData(33,int(time.time())) newItem.setFont(font) @@ -1441,6 +1464,11 @@ class MyForm(QtGui.QMainWindow): shared.config.set('bitmessagesettings', 'defaultnoncetrialsperbyte',str(int(float(self.settingsDialogInstance.ui.lineEditTotalDifficulty.text())*shared.networkDefaultProofOfWorkNonceTrialsPerByte))) if float(self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) >= 1: shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes',str(int(float(self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text())*shared.networkDefaultPayloadLengthExtraBytes))) + if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text()) == 0: + shared.config.set('bitmessagesettings', 'maxacceptablenoncetrialsperbyte',str(int(float(self.settingsDialogInstance.ui.lineEditMaxAcceptableTotalDifficulty.text())*shared.networkDefaultProofOfWorkNonceTrialsPerByte))) + if float(self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1 or float(self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0: + shared.config.set('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes',str(int(float(self.settingsDialogInstance.ui.lineEditMaxAcceptableSmallMessageDifficulty.text())*shared.networkDefaultPayloadLengthExtraBytes))) + #if str(self.settingsDialogInstance.ui.comboBoxMaxCores.currentText()) == 'All': # shared.config.set('bitmessagesettings', 'maxcores', '99999') #else: @@ -1766,6 +1794,25 @@ class MyForm(QtGui.QMainWindow): else: self.ui.tableWidgetSent.selectRow(currentRow-1) + def on_action_ForceSend(self): + currentRow = self.ui.tableWidgetSent.currentRow() + addressAtCurrentRow = str(self.ui.tableWidgetSent.item(currentRow,0).data(Qt.UserRole).toPyObject()) + toRipe = decodeAddress(addressAtCurrentRow)[3] + t = (toRipe,) + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''UPDATE sent SET status='forcepow' WHERE toripe=? AND status='toodifficult' and folder='sent' ''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlSubmitQueue.put('''select ackdata FROM sent WHERE status='forcepow' ''') + shared.sqlSubmitQueue.put('') + queryreturn = shared.sqlReturnQueue.get() + shared.sqlLock.release() + for row in queryreturn: + ackdata, = row + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Overriding maximum-difficulty setting. Work queued.'))) + shared.workerQueue.put(('sendmessage','')) + def on_action_SentClipboard(self): currentRow = self.ui.tableWidgetSent.currentRow() addressAtCurrentRow = str(self.ui.tableWidgetSent.item(currentRow,0).data(Qt.UserRole).toPyObject()) @@ -1975,6 +2022,22 @@ class MyForm(QtGui.QMainWindow): def on_context_menuInbox(self, point): self.popMenuInbox.exec_( self.ui.tableWidgetInbox.mapToGlobal(point) ) def on_context_menuSent(self, point): + self.popMenuSent = QtGui.QMenu( self ) + self.popMenuSent.addAction( self.actionSentClipboard ) + self.popMenuSent.addAction( self.actionTrashSentMessage ) + + #Check to see if this item is toodifficult and display an additional menu option (Force Send) if it is. + currentRow = self.ui.tableWidgetSent.currentRow() + ackData = str(self.ui.tableWidgetSent.item(currentRow,3).data(Qt.UserRole).toPyObject()) + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''SELECT status FROM sent where ackdata=?''') + shared.sqlSubmitQueue.put((ackData,)) + queryreturn = shared.sqlReturnQueue.get() + shared.sqlLock.release() + for row in queryreturn: + status, = row + if status == 'toodifficult': + self.popMenuSent.addAction( self.actionForceSend ) self.popMenuSent.exec_( self.ui.tableWidgetSent.mapToGlobal(point) ) def tableWidgetInboxItemClicked(self): @@ -2152,6 +2215,10 @@ class settingsDialog(QtGui.QDialog): self.ui.lineEditTotalDifficulty.setText(str((float(shared.config.getint('bitmessagesettings', 'defaultnoncetrialsperbyte'))/shared.networkDefaultProofOfWorkNonceTrialsPerByte))) self.ui.lineEditSmallMessageDifficulty.setText(str((float(shared.config.getint('bitmessagesettings', 'defaultpayloadlengthextrabytes'))/shared.networkDefaultPayloadLengthExtraBytes))) + #Max acceptable difficulty tab + self.ui.lineEditMaxAcceptableTotalDifficulty.setText(str((float(shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte'))/shared.networkDefaultProofOfWorkNonceTrialsPerByte))) + self.ui.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes'))/shared.networkDefaultPayloadLengthExtraBytes))) + #'System' tab removed for now. """try: maxCores = shared.config.getint('bitmessagesettings', 'maxcores') diff --git a/src/settings.py b/src/settings.py index d8f86efc..8c94333c 100644 --- a/src/settings.py +++ b/src/settings.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Mon Jun 03 23:09:01 2013 -# by: PyQt4 UI code generator 4.9.5 +# Created: Mon Jun 10 11:31:56 2013 +# by: PyQt4 UI code generator 4.9.4 # # WARNING! All changes made in this file will be lost! @@ -74,6 +74,7 @@ class Ui_settingsDialog(object): self.label.setObjectName(_fromUtf8("label")) self.gridLayout_3.addWidget(self.label, 0, 1, 1, 1) self.lineEditTCPPort = QtGui.QLineEdit(self.groupBox) + self.lineEditTCPPort.setMaximumSize(QtCore.QSize(70, 16777215)) self.lineEditTCPPort.setObjectName(_fromUtf8("lineEditTCPPort")) self.gridLayout_3.addWidget(self.lineEditTCPPort, 0, 2, 1, 1) self.gridLayout_4.addWidget(self.groupBox, 0, 0, 1, 1) @@ -172,6 +173,48 @@ class Ui_settingsDialog(object): self.label_10.setObjectName(_fromUtf8("label_10")) self.gridLayout_6.addWidget(self.label_10, 2, 0, 1, 3) self.tabWidgetSettings.addTab(self.tab, _fromUtf8("")) + self.tab_2 = QtGui.QWidget() + self.tab_2.setObjectName(_fromUtf8("tab_2")) + self.gridLayout_7 = QtGui.QGridLayout(self.tab_2) + self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7")) + self.label_15 = QtGui.QLabel(self.tab_2) + self.label_15.setWordWrap(True) + self.label_15.setObjectName(_fromUtf8("label_15")) + self.gridLayout_7.addWidget(self.label_15, 0, 0, 1, 3) + spacerItem5 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_7.addItem(spacerItem5, 1, 0, 1, 1) + self.label_13 = QtGui.QLabel(self.tab_2) + self.label_13.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_13.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label_13.setObjectName(_fromUtf8("label_13")) + self.gridLayout_7.addWidget(self.label_13, 1, 1, 1, 1) + self.lineEditMaxAcceptableTotalDifficulty = QtGui.QLineEdit(self.tab_2) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.lineEditMaxAcceptableTotalDifficulty.sizePolicy().hasHeightForWidth()) + self.lineEditMaxAcceptableTotalDifficulty.setSizePolicy(sizePolicy) + self.lineEditMaxAcceptableTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215)) + self.lineEditMaxAcceptableTotalDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableTotalDifficulty")) + self.gridLayout_7.addWidget(self.lineEditMaxAcceptableTotalDifficulty, 1, 2, 1, 1) + spacerItem6 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_7.addItem(spacerItem6, 2, 0, 1, 1) + self.label_14 = QtGui.QLabel(self.tab_2) + self.label_14.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label_14.setObjectName(_fromUtf8("label_14")) + self.gridLayout_7.addWidget(self.label_14, 2, 1, 1, 1) + self.lineEditMaxAcceptableSmallMessageDifficulty = QtGui.QLineEdit(self.tab_2) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.lineEditMaxAcceptableSmallMessageDifficulty.sizePolicy().hasHeightForWidth()) + self.lineEditMaxAcceptableSmallMessageDifficulty.setSizePolicy(sizePolicy) + self.lineEditMaxAcceptableSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215)) + self.lineEditMaxAcceptableSmallMessageDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableSmallMessageDifficulty")) + self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1) + spacerItem7 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem7, 3, 1, 1, 1) + self.tabWidgetSettings.addTab(self.tab_2, _fromUtf8("")) self.gridLayout.addWidget(self.tabWidgetSettings, 0, 0, 1, 1) self.retranslateUi(settingsDialog) @@ -222,4 +265,8 @@ class Ui_settingsDialog(object): self.label_12.setText(QtGui.QApplication.translate("settingsDialog", "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.", None, QtGui.QApplication.UnicodeUTF8)) self.label_10.setText(QtGui.QApplication.translate("settingsDialog", "The \'Total difficulty\' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work.", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tab), QtGui.QApplication.translate("settingsDialog", "Demanded difficulty", None, QtGui.QApplication.UnicodeUTF8)) + self.label_15.setText(QtGui.QApplication.translate("settingsDialog", "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.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_13.setText(QtGui.QApplication.translate("settingsDialog", "Maximum acceptable total difficulty:", None, QtGui.QApplication.UnicodeUTF8)) + self.label_14.setText(QtGui.QApplication.translate("settingsDialog", "Maximum acceptable small message difficulty:", None, QtGui.QApplication.UnicodeUTF8)) + self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tab_2), QtGui.QApplication.translate("settingsDialog", "Max acceptable difficulty", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/src/settings.ui b/src/settings.ui index 971480bd..a1bb7c88 100644 --- a/src/settings.ui +++ b/src/settings.ui @@ -142,7 +142,14 @@ - + + + + 70 + 16777215 + + + @@ -373,6 +380,117 @@ + + + Max acceptable 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. + + + true + + + + + + + Qt::Horizontal + + + + 102 + 20 + + + + + + + + Qt::LeftToRight + + + Maximum acceptable total difficulty: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 70 + 16777215 + + + + + + + + Qt::Horizontal + + + + 102 + 20 + + + + + + + + Maximum acceptable small message difficulty: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 70 + 16777215 + + + + + + + + Qt::Vertical + + + + 20 + 147 + + + + + + diff --git a/src/shared.py b/src/shared.py index fbf48b01..d91c99c1 100644 --- a/src/shared.py +++ b/src/shared.py @@ -202,4 +202,12 @@ def flushInventory(): sqlReturnQueue.get() del inventory[hash] sqlSubmitQueue.put('commit') - sqlLock.release() \ No newline at end of file + sqlLock.release() + +def fixPotentiallyInvalidUTF8Data(text): + try: + unicode(text,'utf-8') + return text + except: + output = 'Part of the message is corrupt. The message cannot be displayed the normal way.\n\n' + repr(text) + return output \ No newline at end of file -- 2.45.1 From 7f19ac82d09a55a286299c0dd736c51daba7ef55 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Mon, 10 Jun 2013 23:43:06 -0400 Subject: [PATCH 6/8] Check to see whether we are awaiting a new pubkey within the receiveData thread not the workerThread --- src/bitmessagemain.py | 103 +++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 0c85f339..dbeac1cd 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -668,7 +668,8 @@ class receiveDataThread(threading.Thread): shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') shared.sqlLock.release() - 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. + #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.digest()) fromAddress = encodeAddress(sendersAddressVersion,sendersStream,ripe.digest()) shared.printLock.acquire() @@ -802,7 +803,8 @@ class receiveDataThread(threading.Thread): shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') shared.sqlLock.release() - 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. + #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.digest()) fromAddress = encodeAddress(sendersAddressVersion,sendersStream,ripe.digest()) shared.printLock.acquire() @@ -1051,7 +1053,8 @@ class receiveDataThread(threading.Thread): shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') shared.sqlLock.release() - shared.workerQueue.put(('newpubkey',(sendersAddressVersionNumber,sendersStreamNumber,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. + #shared.workerQueue.put(('newpubkey',(sendersAddressVersionNumber,sendersStreamNumber,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.digest()) fromAddress = encodeAddress(sendersAddressVersionNumber,sendersStreamNumber,ripe.digest()) #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: @@ -1192,6 +1195,23 @@ class receiveDataThread(threading.Thread): else: return '['+mailingListName+'] ' + subject + def possiblyNewPubkey(self,toRipe): + if toRipe in neededPubkeys: + print 'We have been awaiting the arrival of this pubkey.' + del neededPubkeys[toRipe] + t = (toRipe,) + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND status='awaitingpubkey' and folder='sent' ''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + shared.workerQueue.put(('sendmessage','')) + else: + shared.printLock.acquire() + print 'We don\'t need this pub key. We didn\'t ask for it. Pubkey hash:', toRipe.encode('hex') + shared.printLock.release() + #We have received a pubkey def recpubkey(self,data): self.pubkeyProcessingStartTime = time.time() @@ -1318,23 +1338,17 @@ class receiveDataThread(threading.Thread): 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,data,embeddedTime,'yes') - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) else: print 'We have NOT used this pubkey personally. Inserting in database.' t = (ripe,data,embeddedTime,'no') #This will also update the embeddedTime. - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + #shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) + self.possibleNewPubkey(ripe) if addressVersion == 3: if len(data) < 170: #sanity check. print '(within processpubkey) payloadLength less than 170. Sanity check failed.' @@ -1385,22 +1399,17 @@ class receiveDataThread(threading.Thread): 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,data,embeddedTime,'yes') - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() else: print 'We have NOT used this pubkey personally. Inserting in database.' t = (ripe,data,embeddedTime,'no') #This will also update the embeddedTime. - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + #shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) + self.possibleNewPubkey(ripe) #We have received a getpubkey message @@ -2666,23 +2675,23 @@ class singleWorker(threading.Thread): self.doPOWForMyV2Pubkey(data) elif command == 'doPOWForMyV3Pubkey': self.doPOWForMyV3Pubkey(data) - elif command == 'newpubkey': - toAddressVersion,toStreamNumber,toRipe = data - if toRipe in neededPubkeys: - print 'We have been awaiting the arrival of this pubkey.' - del neededPubkeys[toRipe] - t = (toRipe,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND status='awaitingpubkey' and folder='sent' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - self.sendMsg() - else: - shared.printLock.acquire() - print 'We don\'t need this pub key. We didn\'t ask for it. Pubkey hash:', toRipe.encode('hex') - shared.printLock.release() + """elif command == 'newpubkey': + toAddressVersion,toStreamNumber,toRipe = data + if toRipe in neededPubkeys: + print 'We have been awaiting the arrival of this pubkey.' + del neededPubkeys[toRipe] + t = (toRipe,) + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND status='awaitingpubkey' and folder='sent' ''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + self.sendMsg() + else: + shared.printLock.acquire() + print 'We don\'t need this pub key. We didn\'t ask for it. Pubkey hash:', toRipe.encode('hex') + shared.printLock.release()""" else: shared.printLock.acquire() sys.stderr.write('Probable programming error: The command sent to the workerThread is weird. It is: %s\n' % command) -- 2.45.1 From ee0c0f8c9b53be53504a596e7c1af0a0a23bd303 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 11 Jun 2013 02:33:48 -0400 Subject: [PATCH 7/8] Continued Max acceptable difficulty programming and testing --- src/bitmessagemain.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 06670eeb..126c15a8 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -321,8 +321,7 @@ class receiveDataThread(threading.Thread): if self.data[0:4] != '\xe9\xbe\xb4\xd9': if verbose >= 1: shared.printLock.acquire() - sys.stderr.write('The magic bytes were not correct. First 40 bytes of data: %s\n' % repr(self.data[0:40])) - print 'self.data:', self.data.encode('hex') + print 'The magic bytes were not correct. First 40 bytes of data: '+ repr(self.data[0:40]) shared.printLock.release() self.data = "" return @@ -1090,6 +1089,7 @@ class receiveDataThread(threading.Thread): bodyPositionIndex = string.find(message,'\nBody:') if bodyPositionIndex > 1: subject = message[8:bodyPositionIndex] + subject = subject[:500] #Only save and show the first 500 characters of the subject. Any more is probably an attak. body = message[bodyPositionIndex+6:] else: subject = '' @@ -1184,7 +1184,7 @@ class receiveDataThread(threading.Thread): else: return '['+mailingListName+'] ' + subject - def possiblyNewPubkey(self,toRipe): + def possibleNewPubkey(self,toRipe): if toRipe in neededPubkeys: print 'We have been awaiting the arrival of this pubkey.' del neededPubkeys[toRipe] @@ -3014,7 +3014,7 @@ class singleWorker(threading.Thread): ackdataForWhichImWatching[ackdata] = 0 toStatus,toAddressVersionNumber,toStreamNumber,toHash = decodeAddress(toaddress) fromStatus,fromAddressVersionNumber,fromStreamNumber,fromHash = decodeAddress(fromaddress) - shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send the message.'))) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Looking up the receiver\'s public key'))) shared.printLock.acquire() print 'Found a message in our database that needs to be sent with this pubkey.' print 'First 150 characters of message:', repr(message[:150]) @@ -3061,6 +3061,7 @@ class singleWorker(threading.Thread): if toAddressVersionNumber == 2: requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send message. (There is no required difficulty for version 2 addresses like this.)'))) elif toAddressVersionNumber == 3: requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) readPosition += varintLength @@ -3070,12 +3071,13 @@ class singleWorker(threading.Thread): requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes: requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send message. (Receiver\'s required difficulty: '+str(float(requiredAverageProofOfWorkNonceTrialsPerByte)/shared.networkDefaultProofOfWorkNonceTrialsPerByte)+' and '+ str(float(requiredPayloadLengthExtraBytes)/shared.networkDefaultPayloadLengthExtraBytes) + ')'))) if status != 'forcepow': if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') != 0): #The demanded difficulty is more than we are willing to do. shared.sqlLock.acquire() t = (ackdata,) - shared.sqlSubmitQueue.put('''UPDATE sent SET status='toodifficult' WHERE ackdata=? AND status='doingmsgpow' ''') + shared.sqlSubmitQueue.put('''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''') shared.sqlSubmitQueue.put(t) shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') @@ -3173,11 +3175,13 @@ class singleWorker(threading.Thread): powStartTime = time.time() initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) + shared.printLock.acquire() print '(For msg message) 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 + shared.printLock.release() payload = pack('>Q',nonce) + payload inventoryHash = calculateInventoryHash(payload) @@ -3189,8 +3193,8 @@ class singleWorker(threading.Thread): #Update the status of the message in the 'sent' table to have a 'msgsent' status shared.sqlLock.acquire() - t = (toaddress, fromaddress, subject, message) - shared.sqlSubmitQueue.put('''UPDATE sent SET status='msgsent' WHERE toaddress=? AND fromaddress=? AND subject=? AND message=? AND status='doingmsgpow' or status='forcepow' ''') + t = (ackdata,) + shared.sqlSubmitQueue.put('''UPDATE sent SET status='msgsent' WHERE ackdata=? AND status='doingmsgpow' or status='forcepow' ''') shared.sqlSubmitQueue.put(t) queryreturn = shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') -- 2.45.1 From 087d1b8c68e19527ece3c958b9ac33d3ed5e92b0 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 11 Jun 2013 14:15:17 -0400 Subject: [PATCH 8/8] Continued Max acceptable difficulty programming and testing --- src/bitmessagemain.py | 40 ++++++++++++++++++------------------ src/bitmessageqt/__init__.py | 15 +++++++++++--- src/messages.dat reader.py | 6 +++--- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 126c15a8..f0c6e402 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -2465,7 +2465,9 @@ class sqlThread(threading.Thread): self.conn.commit() elif item == 'exit': self.conn.close() + shared.printLock.acquire() print 'sqlThread exiting gracefully.' + shared.printLock.release() return elif item == 'movemessagstoprog': shared.printLock.acquire() @@ -2507,7 +2509,6 @@ class sqlThread(threading.Thread): shared.sqlReturnQueue.put(self.cur.fetchall()) #shared.sqlSubmitQueue.task_done() - '''The singleCleaner class is a timer-driven thread that cleans data structures to free memory, resends messages when a remote node doesn't respond, and sends pong messages to keep connections alive if the network isn't busy. @@ -2983,14 +2984,14 @@ class singleWorker(threading.Thread): shared.UISignalQueue.put(('updateSentItemStatusByHash',(toripe,'Sending a request for the recipient\'s encryption key.'))) self.requestPubKey(toaddress) shared.sqlLock.acquire() + #Get all messages that are ready to be sent, and also all messages which we have sent in the last 28 days which were previously marked as 'toodifficult'. If the user as raised the maximum acceptable difficulty then those messages may now be sendable. shared.sqlSubmitQueue.put('''SELECT toaddress, toripe, fromaddress, subject, message, ackdata, status FROM sent WHERE (status='doingmsgpow' or status='forcepow' or (status='toodifficult' and lastactiontime>?)) and folder='sent' ''') shared.sqlSubmitQueue.put((int(time.time())-2419200,)) queryreturn = shared.sqlReturnQueue.get() shared.sqlLock.release() - for row in queryreturn: + for row in queryreturn: #For each message we need to send.. toaddress, toripe, fromaddress, subject, message, ackdata, status = row - - #Evidently there is a remote possibility that we may no longer have the recipient's pubkey. Let us make sure we still have it or else the sendMsg function will appear to freeze. + #There is a remote possibility that we may no longer have the recipient's pubkey. Let us make sure we still have it or else the sendMsg function will appear to freeze. This can happen if the user sends a message but doesn't let the POW function finish, then leaves their client off for a long time which could cause the needed pubkey to expire and be deleted. shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''SELECT hash FROM pubkeys WHERE hash=? ''') shared.sqlSubmitQueue.put((toripe,)) @@ -3020,17 +3021,17 @@ class singleWorker(threading.Thread): print 'First 150 characters of message:', repr(message[:150]) shared.printLock.release() - #Now let us fetch the recipient's public key out of our database. If the required proof of work difficulty is too hard then we'll abort. + #mark the pubkey as 'usedpersonally' so that we don't ever delete it. shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('SELECT transmitdata FROM pubkeys WHERE hash=?') - shared.sqlSubmitQueue.put((toripe,)) - queryreturn = shared.sqlReturnQueue.get() - #also mark the pubkey as 'usedpersonally' so that we don't delete it. t = (toripe,) shared.sqlSubmitQueue.put('''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=?''') shared.sqlSubmitQueue.put(t) shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') + #Let us fetch the recipient's public key out of our database. If the required proof of work difficulty is too hard then we'll abort. + shared.sqlSubmitQueue.put('SELECT transmitdata FROM pubkeys WHERE hash=?') + shared.sqlSubmitQueue.put((toripe,)) + queryreturn = shared.sqlReturnQueue.get() shared.sqlLock.release() if queryreturn == []: shared.printLock.acquire() @@ -3040,7 +3041,7 @@ class singleWorker(threading.Thread): for row in queryreturn: pubkeyPayload, = row - #The pubkey is stored the way we originally received it which means that we need to read beyond things like the nonce and time to get to the public keys. + #The pubkey message is stored the way we originally received it which means that we need to read beyond things like the nonce and time to get to the actual public keys. readPosition = 8 #to bypass the nonce pubkeyEmbeddedTime, = unpack('>I',pubkeyPayload[readPosition:readPosition+4]) #This section is used for the transition from 32 bit time to 64 bit time in the protocol. @@ -3061,7 +3062,7 @@ class singleWorker(threading.Thread): if toAddressVersionNumber == 2: requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes - shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send message. (There is no required difficulty for version 2 addresses like this.)'))) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send message. (There is no required difficulty for version 2 addresses like this.)'))) elif toAddressVersionNumber == 3: requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) readPosition += varintLength @@ -3071,7 +3072,7 @@ class singleWorker(threading.Thread): requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes: requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes - shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send message. (Receiver\'s required difficulty: '+str(float(requiredAverageProofOfWorkNonceTrialsPerByte)/shared.networkDefaultProofOfWorkNonceTrialsPerByte)+' and '+ str(float(requiredPayloadLengthExtraBytes)/shared.networkDefaultPayloadLengthExtraBytes) + ')'))) + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Doing work necessary to send message.\nReceiver\'s required difficulty: '+str(float(requiredAverageProofOfWorkNonceTrialsPerByte)/shared.networkDefaultProofOfWorkNonceTrialsPerByte)+' and '+ str(float(requiredPayloadLengthExtraBytes)/shared.networkDefaultPayloadLengthExtraBytes)))) if status != 'forcepow': if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings','maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings','maxacceptablepayloadlengthextrabytes') != 0): #The demanded difficulty is more than we are willing to do. @@ -3166,14 +3167,13 @@ class singleWorker(threading.Thread): #We have assembled the data that will be encrypted. encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex')) - #We will now drop the unencrypted data in payload since it has already been encrypted and replace it with the encrypted payload that we will send out. - payload = embeddedTime + encodeVarint(toStreamNumber) + encrypted - target = 2**64 / ((len(payload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte) + encryptedPayload = embeddedTime + encodeVarint(toStreamNumber) + encrypted + target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte) shared.printLock.acquire() print '(For msg message) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte)/shared.networkDefaultProofOfWorkNonceTrialsPerByte,'Required small message difficulty:', float(requiredPayloadLengthExtraBytes)/shared.networkDefaultPayloadLengthExtraBytes shared.printLock.release() powStartTime = time.time() - initialHash = hashlib.sha512(payload).digest() + initialHash = hashlib.sha512(encryptedPayload).digest() trialValue, nonce = proofofwork.run(target, initialHash) shared.printLock.acquire() print '(For msg message) Found proof of work', trialValue, 'Nonce:', nonce @@ -3182,11 +3182,11 @@ class singleWorker(threading.Thread): except: pass shared.printLock.release() - payload = pack('>Q',nonce) + payload + encryptedPayload = pack('>Q',nonce) + encryptedPayload - inventoryHash = calculateInventoryHash(payload) + inventoryHash = calculateInventoryHash(encryptedPayload) objectType = 'msg' - shared.inventory[inventoryHash] = (objectType, toStreamNumber, payload, int(time.time())) + shared.inventory[inventoryHash] = (objectType, toStreamNumber, encryptedPayload, int(time.time())) shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,'Message sent. Waiting on acknowledgement. Sent on ' + unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))) print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues((streamNumber, 'sendinv', inventoryHash)) @@ -3194,7 +3194,7 @@ class singleWorker(threading.Thread): #Update the status of the message in the 'sent' table to have a 'msgsent' status shared.sqlLock.acquire() t = (ackdata,) - shared.sqlSubmitQueue.put('''UPDATE sent SET status='msgsent' WHERE ackdata=? AND status='doingmsgpow' or status='forcepow' ''') + shared.sqlSubmitQueue.put('''UPDATE sent SET status='msgsent' WHERE ackdata=? AND (status='doingmsgpow' or status='forcepow') ''') shared.sqlSubmitQueue.put(t) queryreturn = shared.sqlReturnQueue.get() shared.sqlSubmitQueue.put('commit') diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 356d3e2e..38ef97b0 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -33,6 +33,7 @@ import os from pyelliptic.openssl import OpenSSL import pickle import platform +import string class MyForm(QtGui.QMainWindow): @@ -935,8 +936,13 @@ class MyForm(QtGui.QMainWindow): toAddress = str(self.ui.tableWidgetSent.item(i,0).data(Qt.UserRole).toPyObject()) status,addressVersionNumber,streamNumber,ripe = decodeAddress(toAddress) if ripe == toRipe: - self.ui.tableWidgetSent.item(i,3).setText(textToDisplay) self.ui.tableWidgetSent.item(i,3).setToolTip(textToDisplay) + parenPositionIndex = string.find(textToDisplay,'\n') + if parenPositionIndex > 1: + self.ui.tableWidgetSent.item(i,3).setText(textToDisplay[:parenPositionIndex]) + else: + self.ui.tableWidgetSent.item(i,3).setText(textToDisplay) + def updateSentItemStatusByAckdata(self,ackdata,textToDisplay): for i in range(self.ui.tableWidgetSent.rowCount()): @@ -944,9 +950,12 @@ class MyForm(QtGui.QMainWindow): tableAckdata = self.ui.tableWidgetSent.item(i,3).data(Qt.UserRole).toPyObject() status,addressVersionNumber,streamNumber,ripe = decodeAddress(toAddress) if ackdata == tableAckdata: - #self.ui.tableWidgetSent.item(i,3).setText(unicode(textToDisplay,'utf-8')) - self.ui.tableWidgetSent.item(i,3).setText(textToDisplay) self.ui.tableWidgetSent.item(i,3).setToolTip(textToDisplay) + parenPositionIndex = string.find(textToDisplay,'\n') + if parenPositionIndex > 1: + self.ui.tableWidgetSent.item(i,3).setText(textToDisplay[:parenPositionIndex]) + else: + self.ui.tableWidgetSent.item(i,3).setText(textToDisplay) def removeInboxRowByMsgid(self,msgid):#msgid and inventoryHash are the same thing for i in range(self.ui.tableWidgetInbox.rowCount()): diff --git a/src/messages.dat reader.py b/src/messages.dat reader.py index 286739e0..b5ade9ff 100644 --- a/src/messages.dat reader.py +++ b/src/messages.dat reader.py @@ -34,7 +34,7 @@ def readInbox(): def readSent(): print 'Printing everything in Sent table:' - item = '''select * from sent''' + item = '''select * from sent where folder !='trash' ''' parameters = '' cur.execute(item, parameters) output = cur.fetchall() @@ -107,8 +107,8 @@ def vacuum(): #takeInboxMessagesOutOfTrash() #takeSentMessagesOutOfTrash() #markAllInboxMessagesAsUnread() -readInbox() -#readSent() +#readInbox() +readSent() #readPubkeys() #readSubscriptions() #readInventory() -- 2.45.1