Exemple #1
0
    def test (self):
        try:
            if self.nmctype == "namecoind":
                res = self.callRPC ("getinfo", [])
                vers = res["version"]
                
                v3 = vers % 100
                vers = vers / 100
                v2 = vers % 100
                vers = vers / 100
                v1 = vers
                if v3 == 0:
                  versStr = "0.%d.%d" % (v1, v2)
                else:
                  versStr = "0.%d.%d.%d" % (v1, v2, v3)
                return ('success',  tr.translateText("MainWindow",'Success!  Namecoind version %1 running.').arg(unicode(versStr)) )

            elif self.nmctype == "nmcontrol":
                res = self.callRPC ("data", ["status"])
                prefix = "Plugin data running"
                if ("reply" in res) and res["reply"][:len(prefix)] == prefix:
                    return ('success', tr.translateText("MainWindow",'Success!  NMControll is up and running.'))

                print "Unexpected nmcontrol reply: %s" % res
                return ('failed',  tr.translateText("MainWindow",'Couldn\'t understand NMControl.'))

            else:
                assert False

        except Exception as exc:
            print "Exception testing the namecoin connection:\n%s" % str (exc)
            return ('failed', "The connection to namecoin failed.")
Exemple #2
0
    def query (self, string):
        slashPos = string.find ("/")
        if slashPos < 0:
            string = "id/" + string

        try:
            if self.nmctype == "namecoind":
                res = self.callRPC ("name_show", [string])
                res = res["value"]
            elif self.nmctype == "nmcontrol":
                res = self.callRPC ("data", ["getValue", string])
                res = res["reply"]
                if res == False:
                    raise RPCError ({"code": -4})
            else:
                assert False
        except RPCError as exc:
            if exc.error["code"] == -4:
                return (tr.translateText("MainWindow",'The name %1 was not found.').arg(unicode(string)), None)
            else:
                return (tr.translateText("MainWindow",'The namecoin query failed (%1)').arg(unicode(exc.error["message"])), None)
        except Exception as exc:
            print "Namecoin query exception: %s" % str (exc)
            return (tr.translateText("MainWindow",'The namecoin query failed.'), None)

        try:
            val = json.loads (res)
        except:
            return (tr.translateText("MainWindow",'The name %1 has no valid JSON data.').arg(unicode(string)), None)            

        if "bitmessage" in val:
            return (None, val["bitmessage"])
        return (tr.translateText("MainWindow",'The name %1 has no associated Bitmessage address.').arg(unicode(string)), None) 
    def requestPubKey(self, toAddress):
        toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress(
            toAddress)
        if toStatus != 'success':
            with shared.printLock:
                sys.stderr.write('Very abnormal error occurred in requestPubKey. toAddress is: ' + repr(
                    toAddress) + '. Please report this error to Atheros.')

            return
        shared.neededPubkeys[ripe] = 0
        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
        with shared.printLock:
            print 'making request for pubkey with ripe:', ripe.encode('hex')

        # print 'trial value', trialValue
        statusbar = 'Doing the computations necessary to request the recipient\'s public key.'
        shared.UISignalQueue.put(('updateStatusBar', statusbar))
        shared.UISignalQueue.put(('updateSentItemStatusByHash', (
            ripe, tr.translateText("MainWindow",'Doing work necessary to request encryption key.'))))
        target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes +
                             8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)
        initialHash = hashlib.sha512(payload).digest()
        trialValue, nonce = proofofwork.run(target, initialHash)
        with shared.printLock:
            print 'Found proof of work', trialValue, 'Nonce:', nonce


        payload = pack('>Q', nonce) + payload
        inventoryHash = calculateInventoryHash(payload)
        objectType = 'getpubkey'
        shared.inventory[inventoryHash] = (
            objectType, streamNumber, payload, int(time.time()))
        print 'sending inv (for the getpubkey message)'
        shared.broadcastToSendDataQueues((
            streamNumber, 'sendinv', inventoryHash))

        t = (toAddress,)
        shared.sqlLock.acquire()
        shared.sqlSubmitQueue.put(
            '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='doingpubkeypow' ''')
        shared.sqlSubmitQueue.put(t)
        shared.sqlReturnQueue.get()
        shared.sqlSubmitQueue.put('commit')
        shared.sqlLock.release()

        shared.UISignalQueue.put((
            'updateStatusBar', tr.translateText("MainWindow",'Broacasting the public key request. This program will auto-retry if they are offline.')))
        shared.UISignalQueue.put(('updateSentItemStatusByHash', (ripe, tr.translateText("MainWindow",'Sending public key request. Waiting for reply. Requested at %1').arg(unicode(
            strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
    def requestPubKey(self, toAddress):
        toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress(
            toAddress)
        if toStatus != 'success':
            with shared.printLock:
                sys.stderr.write('Very abnormal error occurred in requestPubKey. toAddress is: ' + repr(
                    toAddress) + '. Please report this error to Atheros.')

            return
        shared.neededPubkeys[ripe] = 0
        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
        with shared.printLock:
            print 'making request for pubkey with ripe:', ripe.encode('hex')

        # print 'trial value', trialValue
        statusbar = 'Doing the computations necessary to request the recipient\'s public key.'
        shared.UISignalQueue.put(('updateStatusBar', statusbar))
        shared.UISignalQueue.put(('updateSentItemStatusByHash', (
            ripe, tr.translateText("MainWindow",'Doing work necessary to request encryption key.'))))
        target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes +
                             8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)
        initialHash = hashlib.sha512(payload).digest()
        trialValue, nonce = proofofwork.run(target, initialHash)
        with shared.printLock:
            print 'Found proof of work', trialValue, 'Nonce:', nonce


        payload = pack('>Q', nonce) + payload
        inventoryHash = calculateInventoryHash(payload)
        objectType = 'getpubkey'
        shared.inventory[inventoryHash] = (
            objectType, streamNumber, payload, int(time.time()))
        print 'sending inv (for the getpubkey message)'
        shared.broadcastToSendDataQueues((
            streamNumber, 'sendinv', inventoryHash))

        t = (toAddress,)
        shared.sqlLock.acquire()
        shared.sqlSubmitQueue.put(
            '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='doingpubkeypow' ''')
        shared.sqlSubmitQueue.put(t)
        shared.sqlReturnQueue.get()
        shared.sqlSubmitQueue.put('commit')
        shared.sqlLock.release()

        shared.UISignalQueue.put((
            'updateStatusBar', tr.translateText("MainWindow",'Broacasting the public key request. This program will auto-retry if they are offline.')))
        shared.UISignalQueue.put(('updateSentItemStatusByHash', (ripe, tr.translateText("MainWindow",'Sending public key request. Waiting for reply. Requested at %1').arg(unicode(
            strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
Exemple #5
0
 def run(self):
     from debug import logger
     
     logger.debug("Starting UPnP thread")
     logger.debug("Local IP: %s", self.localIP)
     lastSent = 0
     while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'):
         if time.time() - lastSent > self.sendSleep and len(self.routers) == 0:
             try:
                 self.sendSearchRouter()
             except:
                 pass
             lastSent = time.time()
         try:
             while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'):
                 resp,(ip,port) = self.sock.recvfrom(1000)
                 if resp is None:
                     continue
                 newRouter = Router(resp, ip)
                 for router in self.routers:
                     if router.location == newRouter.location:
                         break
                 else:
                     logger.debug("Found UPnP router at %s", ip)
                     self.routers.append(newRouter)
                     self.createPortMapping(newRouter)
                     shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping established on port %1').arg(str(self.extPort))))
                     break
         except socket.timeout as e:
             pass
         except:
             logger.error("Failure running UPnP router search.", exc_info=True)
         for router in self.routers:
             if router.extPort is None:
                 self.createPortMapping(router)
     try:
         self.sock.shutdown(socket.SHUT_RDWR)
     except:
         pass
     try:
         self.sock.close()
     except:
         pass
     deleted = False
     for router in self.routers:
         if router.extPort is not None:
             deleted = True
             self.deletePortMapping(router)
     shared.extPort = None
     if deleted:
         shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping removed')))
     logger.debug("UPnP thread done")
Exemple #6
0
 def run(self):
     from debug import logger
     
     logger.debug("Starting UPnP thread")
     lastSent = 0
     while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'):
         if time.time() - lastSent > self.sendSleep and len(self.routers) == 0:
             self.sendSearchRouter()
             lastSent = time.time()
         try:
             while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'):
                 resp,(ip,port) = self.sock.recvfrom(1000)
                 if resp is None:
                     continue
                 newRouter = Router(resp, ip)
                 for router in self.routers:
                     if router.location == newRouter.location:
                         break
                 else:
                     logger.debug("Found UPnP router at %s", ip)
                     self.routers.append(newRouter)
                     self.createPortMapping(newRouter)
                     shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping established on port %1').arg(str(self.extPort))))
                     break
         except socket.timeout as e:
             pass
         except:
             logger.error("Failure running UPnP router search.", exc_info=True)
         for router in self.routers:
             if router.extPort is None:
                 self.createPortMapping(router)
     try:
         self.sock.shutdown(socket.SHUT_RDWR)
     except:
         pass
     try:
         self.sock.close()
     except:
         pass
     deleted = False
     for router in self.routers:
         if router.extPort is not None:
             deleted = True
             self.deletePortMapping(router)
     shared.extPort = None
     if deleted:
         shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping removed')))
     logger.debug("UPnP thread done")
def _doGPUPoW(target, initialHash):
    logger.debug("GPU PoW start")
    nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
    trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
    #print "{} - value {} < {}".format(nonce, trialValue, target)
    if trialValue > target:
        deviceNames = ", ".join(gpu.name for gpu in openclpow.gpus)
        UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.')))
        logger.error("Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.", deviceNames)
        openclpow.ctx = False
        raise Exception("GPU did not calculate correctly.")
    logger.debug("GPU PoW done")
    return [trialValue, nonce]
Exemple #8
0
def _doGPUPoW(target, initialHash):
    logger.debug("GPU PoW start")
    nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
    trialValue, = unpack(
        '>Q',
        hashlib.sha512(
            hashlib.sha512(pack('>Q', nonce) +
                           initialHash).digest()).digest()[0:8])
    #print "{} - value {} < {}".format(nonce, trialValue, target)
    if trialValue > target:
        deviceNames = ", ".join(gpu.name for gpu in openclpow.gpus)
        UISignalQueue.put((
            'updateStatusBar',
            tr.translateText(
                "MainWindow",
                'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.'
            )))
        logger.error(
            "Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.",
            deviceNames)
        openclpow.ctx = False
        raise Exception("GPU did not calculate correctly.")
    logger.debug("GPU PoW done")
    return [trialValue, nonce]
    def run(self):
        while shared.safeConfigGetBoolean("bitmessagesettings", "dontconnect") and not self._stopped:
            self.stop.wait(2)
        while shared.safeConfigGetBoolean("bitmessagesettings", "sendoutgoingconnections") and not self._stopped:
            self.name = "outgoingSynSender"
            maximumConnections = 1 if shared.trustedPeer else 8  # maximum number of outgoing connections = 8
            while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections:
                self.stop.wait(10)
            if shared.shutdown:
                break
            random.seed()
            peer = self._getPeer()
            shared.alreadyAttemptedConnectionsListLock.acquire()
            while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
                shared.alreadyAttemptedConnectionsListLock.release()
                # print 'choosing new sample'
                random.seed()
                peer = self._getPeer()
                self.stop.wait(1)
                if shared.shutdown:
                    break
                # Clear out the shared.alreadyAttemptedConnectionsList every half
                # hour so that this program will again attempt a connection
                # to any nodes, even ones it has already tried.
                if (time.time() - shared.alreadyAttemptedConnectionsListResetTime) > 1800:
                    shared.alreadyAttemptedConnectionsList.clear()
                    shared.alreadyAttemptedConnectionsListResetTime = int(time.time())
                shared.alreadyAttemptedConnectionsListLock.acquire()
            shared.alreadyAttemptedConnectionsList[peer] = 0
            try:
                shared.alreadyAttemptedConnectionsListLock.release()
            except threading.ThreadError as e:
                pass
            self.name = "outgoingSynSender-" + peer.host.replace(":", ".")  # log parser field separator
            if peer.host.find(":") == -1:
                address_family = socket.AF_INET
            else:
                address_family = socket.AF_INET6
            try:
                self.sock = socks.socksocket(address_family, socket.SOCK_STREAM)
            except:
                """
                The line can fail on Windows systems which aren't
                64-bit compatiable:
                      File "C:\Python27\lib\socket.py", line 187, in __init__
                        _sock = _realsocket(family, type, proto)
                      error: [Errno 10047] An address incompatible with the requested protocol was used
                      
                So let us remove the offending address from our knownNodes file.
                """
                shared.knownNodesLock.acquire()
                try:
                    del shared.knownNodes[self.streamNumber][peer]
                except:
                    pass
                shared.knownNodesLock.release()
                logger.debug(
                    "deleting "
                    + str(peer)
                    + " from shared.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible."
                )
                continue
            # This option apparently avoids the TIME_WAIT state so that we
            # can rebind faster
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.settimeout(20)
            if shared.config.get("bitmessagesettings", "socksproxytype") == "none" and shared.verbose >= 2:
                logger.debug("Trying an outgoing connection to " + str(peer))

                # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            elif shared.config.get("bitmessagesettings", "socksproxytype") == "SOCKS4a":
                if shared.verbose >= 2:
                    logger.debug("(Using SOCKS4a) Trying an outgoing connection to " + str(peer))

                proxytype = socks.PROXY_TYPE_SOCKS4
                sockshostname = shared.config.get("bitmessagesettings", "sockshostname")
                socksport = shared.config.getint("bitmessagesettings", "socksport")
                rdns = (
                    True
                )  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean("bitmessagesettings", "socksauthentication"):
                    socksusername = shared.config.get("bitmessagesettings", "socksusername")
                    sockspassword = shared.config.get("bitmessagesettings", "sockspassword")
                    self.sock.setproxy(proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
                else:
                    self.sock.setproxy(proxytype, sockshostname, socksport, rdns)
            elif shared.config.get("bitmessagesettings", "socksproxytype") == "SOCKS5":
                if shared.verbose >= 2:
                    logger.debug("(Using SOCKS5) Trying an outgoing connection to " + str(peer))

                proxytype = socks.PROXY_TYPE_SOCKS5
                sockshostname = shared.config.get("bitmessagesettings", "sockshostname")
                socksport = shared.config.getint("bitmessagesettings", "socksport")
                rdns = (
                    True
                )  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean("bitmessagesettings", "socksauthentication"):
                    socksusername = shared.config.get("bitmessagesettings", "socksusername")
                    sockspassword = shared.config.get("bitmessagesettings", "sockspassword")
                    self.sock.setproxy(proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
                else:
                    self.sock.setproxy(proxytype, sockshostname, socksport, rdns)

            try:
                self.sock.connect((peer.host, peer.port))
                someObjectsOfWhichThisRemoteNodeIsAlreadyAware = (
                    {}
                )  # This is not necessairly a complete list; we clear it from time to time to save memory.
                sendDataThreadQueue = (
                    Queue.Queue()
                )  # Used to submit information to the send data thread for this connection.

                sd = sendDataThread(sendDataThreadQueue)
                sd.setup(
                    self.sock, peer.host, peer.port, self.streamNumber, someObjectsOfWhichThisRemoteNodeIsAlreadyAware
                )
                sd.start()

                rd = receiveDataThread()
                rd.daemon = True  # close the main program even if there are threads left
                rd.setup(
                    self.sock,
                    peer.host,
                    peer.port,
                    self.streamNumber,
                    someObjectsOfWhichThisRemoteNodeIsAlreadyAware,
                    self.selfInitiatedConnections,
                    sendDataThreadQueue,
                    sd.objectHashHolderInstance,
                )
                rd.start()

                sd.sendVersionMessage()

                logger.debug(str(self) + " connected to " + str(peer) + " during an outgoing attempt.")
            except socks.GeneralProxyError as err:
                if shared.verbose >= 2:
                    logger.debug("Could NOT connect to " + str(peer) + " during outgoing attempt. " + str(err))

                deletedPeer = None
                with shared.knownNodesLock:
                    """
                    It is remotely possible that peer is no longer in shared.knownNodes.
                    This could happen if two outgoingSynSender threads both try to 
                    connect to the same peer, both fail, and then both try to remove
                    it from shared.knownNodes. This is unlikely because of the
                    alreadyAttemptedConnectionsList but because we clear that list once
                    every half hour, it can happen.
                    """
                    if peer in shared.knownNodes[self.streamNumber]:
                        timeLastSeen = shared.knownNodes[self.streamNumber][peer]
                        if (int(time.time()) - timeLastSeen) > 172800 and len(
                            shared.knownNodes[self.streamNumber]
                        ) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the shared.knownNodes data-structure.
                            del shared.knownNodes[self.streamNumber][peer]
                            deletedPeer = peer
                if deletedPeer:
                    str(
                        "deleting "
                        + str(peer)
                        + " from shared.knownNodes because it is more than 48 hours old and we could not connect to it."
                    )

            except socks.Socks5AuthError as err:
                shared.UISignalQueue.put(
                    (
                        "updateStatusBar",
                        tr.translateText("MainWindow", "SOCKS5 Authentication problem: %1").arg(str(err)),
                    )
                )
            except socks.Socks5Error as err:
                pass
                logger.error("SOCKS5 error. (It is possible that the server wants authentication).) " + str(err))
            except socks.Socks4Error as err:
                logger.error("Socks4Error: " + str(err))
            except socket.error as err:
                if shared.config.get("bitmessagesettings", "socksproxytype")[0:5] == "SOCKS":
                    logger.error("Bitmessage MIGHT be having trouble connecting to the SOCKS server. " + str(err))
                else:
                    if shared.verbose >= 1:
                        logger.debug("Could NOT connect to " + str(peer) + "during outgoing attempt. " + str(err))

                deletedPeer = None
                with shared.knownNodesLock:
                    """
                    It is remotely possible that peer is no longer in shared.knownNodes.
                    This could happen if two outgoingSynSender threads both try to 
                    connect to the same peer, both fail, and then both try to remove
                    it from shared.knownNodes. This is unlikely because of the
                    alreadyAttemptedConnectionsList but because we clear that list once
                    every half hour, it can happen.
                    """
                    if peer in shared.knownNodes[self.streamNumber]:
                        timeLastSeen = shared.knownNodes[self.streamNumber][peer]
                        if (int(time.time()) - timeLastSeen) > 172800 and len(
                            shared.knownNodes[self.streamNumber]
                        ) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the shared.knownNodes data-structure.
                            del shared.knownNodes[self.streamNumber][peer]
                            deletedPeer = peer
                if deletedPeer:
                    logger.debug(
                        "deleting "
                        + str(peer)
                        + " from shared.knownNodes because it is more than 48 hours old and we could not connect to it."
                    )

            except Exception as err:
                import traceback

                logger.exception(
                    "An exception has occurred in the outgoingSynSender thread that was not caught by other exception types:"
                )
            self.stop.wait(0.1)
Exemple #10
0
    def run(self):
        while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect') and not self._stopped:
            self.stop.wait(2)
        while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections') and not self._stopped:
            self.name = "outgoingSynSender"
            maximumConnections = 1 if shared.trustedPeer else 8 # maximum number of outgoing connections = 8
            while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections:
                self.stop.wait(10)
            if shared.shutdown:
                break
            random.seed()
            peer = self._getPeer()
            shared.alreadyAttemptedConnectionsListLock.acquire()
            while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
                shared.alreadyAttemptedConnectionsListLock.release()
                # print 'choosing new sample'
                random.seed()
                peer = self._getPeer()
                self.stop.wait(1)
                if shared.shutdown:
                    break
                # Clear out the shared.alreadyAttemptedConnectionsList every half
                # hour so that this program will again attempt a connection
                # to any nodes, even ones it has already tried.
                if (time.time() - shared.alreadyAttemptedConnectionsListResetTime) > 1800:
                    shared.alreadyAttemptedConnectionsList.clear()
                    shared.alreadyAttemptedConnectionsListResetTime = int(
                        time.time())
                shared.alreadyAttemptedConnectionsListLock.acquire()
            shared.alreadyAttemptedConnectionsList[peer] = 0
            try:
                shared.alreadyAttemptedConnectionsListLock.release()
            except threading.ThreadError as e:
                pass
            self.name = "outgoingSynSender-" + peer.host
            if peer.host.find(':') == -1:
                address_family = socket.AF_INET
            else:
                address_family = socket.AF_INET6
            try:
                self.sock = socks.socksocket(address_family, socket.SOCK_STREAM)
            except:
                """
                The line can fail on Windows systems which aren't
                64-bit compatiable:
                      File "C:\Python27\lib\socket.py", line 187, in __init__
                        _sock = _realsocket(family, type, proto)
                      error: [Errno 10047] An address incompatible with the requested protocol was used
                      
                So let us remove the offending address from our knownNodes file.
                """
                shared.knownNodesLock.acquire()
                try:
                    del shared.knownNodes[self.streamNumber][peer]
                except:
                    pass
                shared.knownNodesLock.release()
                logger.debug('deleting ' + str(peer) + ' from shared.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible.')
                continue
            # This option apparently avoids the TIME_WAIT state so that we
            # can rebind faster
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.settimeout(20)
            if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2:
                logger.debug('Trying an outgoing connection to ' + str(peer))

                # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS4a':
                if shared.verbose >= 2:
                    logger.debug ('(Using SOCKS4a) Trying an outgoing connection to ' + str(peer))

                proxytype = socks.PROXY_TYPE_SOCKS4
                sockshostname = shared.config.get(
                    'bitmessagesettings', 'sockshostname')
                socksport = shared.config.getint(
                    'bitmessagesettings', 'socksport')
                rdns = True  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean('bitmessagesettings', 'socksauthentication'):
                    socksusername = shared.config.get(
                        'bitmessagesettings', 'socksusername')
                    sockspassword = shared.config.get(
                        'bitmessagesettings', 'sockspassword')
                    self.sock.setproxy(
                        proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
                else:
                    self.sock.setproxy(
                        proxytype, sockshostname, socksport, rdns)
            elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
                if shared.verbose >= 2:
                    logger.debug ('(Using SOCKS5) Trying an outgoing connection to ' + str(peer))

                proxytype = socks.PROXY_TYPE_SOCKS5
                sockshostname = shared.config.get(
                    'bitmessagesettings', 'sockshostname')
                socksport = shared.config.getint(
                    'bitmessagesettings', 'socksport')
                rdns = True  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean('bitmessagesettings', 'socksauthentication'):
                    socksusername = shared.config.get(
                        'bitmessagesettings', 'socksusername')
                    sockspassword = shared.config.get(
                        'bitmessagesettings', 'sockspassword')
                    self.sock.setproxy(
                        proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
                else:
                    self.sock.setproxy(
                        proxytype, sockshostname, socksport, rdns)

            try:
                self.sock.connect((peer.host, peer.port))
                rd = receiveDataThread()
                rd.daemon = True  # close the main program even if there are threads left
                someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory.
                sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection. 
                rd.setup(self.sock, 
                         peer.host, 
                         peer.port, 
                         self.streamNumber,
                         someObjectsOfWhichThisRemoteNodeIsAlreadyAware, 
                         self.selfInitiatedConnections, 
                         sendDataThreadQueue)
                rd.start()
                logger.debug(str(self) + ' connected to ' + str(peer) + ' during an outgoing attempt.')


                sd = sendDataThread(sendDataThreadQueue)
                sd.setup(self.sock, peer.host, peer.port, self.streamNumber,
                         someObjectsOfWhichThisRemoteNodeIsAlreadyAware)
                sd.start()
                sd.sendVersionMessage()

            except socks.GeneralProxyError as err:
                if shared.verbose >= 2:
                    logger.debug('Could NOT connect to ' + str(peer) + ' during outgoing attempt. ' + str(err))

                deletedPeer = None
                with shared.knownNodesLock:
                    """
                    It is remotely possible that peer is no longer in shared.knownNodes.
                    This could happen if two outgoingSynSender threads both try to 
                    connect to the same peer, both fail, and then both try to remove
                    it from shared.knownNodes. This is unlikely because of the
                    alreadyAttemptedConnectionsList but because we clear that list once
                    every half hour, it can happen.
                    """
                    if peer in shared.knownNodes[self.streamNumber]:
                        timeLastSeen = shared.knownNodes[self.streamNumber][peer]
                        if (int(time.time()) - timeLastSeen) > 172800 and len(shared.knownNodes[self.streamNumber]) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the shared.knownNodes data-structure.
                            del shared.knownNodes[self.streamNumber][peer]
                            deletedPeer = peer
                if deletedPeer:
                    str ('deleting ' + str(peer) + ' from shared.knownNodes because it is more than 48 hours old and we could not connect to it.')

            except socks.Socks5AuthError as err:
                shared.UISignalQueue.put((
                    'updateStatusBar', tr.translateText(
                    "MainWindow", "SOCKS5 Authentication problem: %1").arg(str(err))))
            except socks.Socks5Error as err:
                pass
                logger.error('SOCKS5 error. (It is possible that the server wants authentication).) ' + str(err))
            except socks.Socks4Error as err:
                logger.error('Socks4Error: ' + str(err))
            except socket.error as err:
                if shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS':
                    logger.error('Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str(err))
                else:
                    if shared.verbose >= 1:
                        logger.debug('Could NOT connect to ' + str(peer) + 'during outgoing attempt. ' + str(err))

                deletedPeer = None
                with shared.knownNodesLock:
                    """
                    It is remotely possible that peer is no longer in shared.knownNodes.
                    This could happen if two outgoingSynSender threads both try to 
                    connect to the same peer, both fail, and then both try to remove
                    it from shared.knownNodes. This is unlikely because of the
                    alreadyAttemptedConnectionsList but because we clear that list once
                    every half hour, it can happen.
                    """
                    if peer in shared.knownNodes[self.streamNumber]:
                        timeLastSeen = shared.knownNodes[self.streamNumber][peer]
                        if (int(time.time()) - timeLastSeen) > 172800 and len(shared.knownNodes[self.streamNumber]) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the shared.knownNodes data-structure.
                            del shared.knownNodes[self.streamNumber][peer]
                            deletedPeer = peer
                if deletedPeer:
                    logger.debug('deleting ' + str(peer) + ' from shared.knownNodes because it is more than 48 hours old and we could not connect to it.')

            except Exception as err:
                import traceback
                logger.exception('An exception has occurred in the outgoingSynSender thread that was not caught by other exception types:')
            self.stop.wait(0.1)
Exemple #11
0
    def run(self):
        while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
            time.sleep(2)
        while shared.safeConfigGetBoolean('bitmessagesettings',
                                          'sendoutgoingconnections'):
            while len(self.selfInitiatedConnections[self.streamNumber]
                      ) >= 8:  # maximum number of outgoing connections = 8
                time.sleep(10)
            if shared.shutdown:
                break
            random.seed()
            shared.knownNodesLock.acquire()
            peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
            shared.knownNodesLock.release()
            shared.alreadyAttemptedConnectionsListLock.acquire()
            while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
                shared.alreadyAttemptedConnectionsListLock.release()
                # print 'choosing new sample'
                random.seed()
                shared.knownNodesLock.acquire()
                peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
                shared.knownNodesLock.release()
                time.sleep(1)
                # Clear out the shared.alreadyAttemptedConnectionsList every half
                # hour so that this program will again attempt a connection
                # to any nodes, even ones it has already tried.
                if (time.time() -
                        shared.alreadyAttemptedConnectionsListResetTime
                    ) > 1800:
                    shared.alreadyAttemptedConnectionsList.clear()
                    shared.alreadyAttemptedConnectionsListResetTime = int(
                        time.time())
                shared.alreadyAttemptedConnectionsListLock.acquire()
            shared.alreadyAttemptedConnectionsList[peer] = 0
            shared.alreadyAttemptedConnectionsListLock.release()
            timeNodeLastSeen = shared.knownNodes[self.streamNumber][peer]
            sock = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM)
            # This option apparently avoids the TIME_WAIT state so that we
            # can rebind faster
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.settimeout(20)
            if shared.config.get(
                    'bitmessagesettings',
                    'socksproxytype') == 'none' and shared.verbose >= 2:
                with shared.printLock:
                    print 'Trying an outgoing connection to', peer

                # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            elif shared.config.get('bitmessagesettings',
                                   'socksproxytype') == 'SOCKS4a':
                if shared.verbose >= 2:
                    with shared.printLock:
                        print '(Using SOCKS4a) Trying an outgoing connection to', peer

                proxytype = socks.PROXY_TYPE_SOCKS4
                sockshostname = shared.config.get('bitmessagesettings',
                                                  'sockshostname')
                socksport = shared.config.getint('bitmessagesettings',
                                                 'socksport')
                rdns = True  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean('bitmessagesettings',
                                            'socksauthentication'):
                    socksusername = shared.config.get('bitmessagesettings',
                                                      'socksusername')
                    sockspassword = shared.config.get('bitmessagesettings',
                                                      'sockspassword')
                    sock.setproxy(proxytype, sockshostname, socksport, rdns,
                                  socksusername, sockspassword)
                else:
                    sock.setproxy(proxytype, sockshostname, socksport, rdns)
            elif shared.config.get('bitmessagesettings',
                                   'socksproxytype') == 'SOCKS5':
                if shared.verbose >= 2:
                    with shared.printLock:
                        print '(Using SOCKS5) Trying an outgoing connection to', peer

                proxytype = socks.PROXY_TYPE_SOCKS5
                sockshostname = shared.config.get('bitmessagesettings',
                                                  'sockshostname')
                socksport = shared.config.getint('bitmessagesettings',
                                                 'socksport')
                rdns = True  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean('bitmessagesettings',
                                            'socksauthentication'):
                    socksusername = shared.config.get('bitmessagesettings',
                                                      'socksusername')
                    sockspassword = shared.config.get('bitmessagesettings',
                                                      'sockspassword')
                    sock.setproxy(proxytype, sockshostname, socksport, rdns,
                                  socksusername, sockspassword)
                else:
                    sock.setproxy(proxytype, sockshostname, socksport, rdns)

            try:
                sock.connect((peer.host, peer.port))
                rd = receiveDataThread()
                rd.daemon = True  # close the main program even if there are threads left
                someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {
                }  # This is not necessairly a complete list; we clear it from time to time to save memory.
                rd.setup(sock, peer.host, peer.port, self.streamNumber,
                         someObjectsOfWhichThisRemoteNodeIsAlreadyAware,
                         self.selfInitiatedConnections)
                rd.start()
                with shared.printLock:
                    print self, 'connected to', peer, 'during an outgoing attempt.'

                sd = sendDataThread()
                sd.setup(sock, peer.host, peer.port, self.streamNumber,
                         someObjectsOfWhichThisRemoteNodeIsAlreadyAware)
                sd.start()
                sd.sendVersionMessage()

            except socks.GeneralProxyError as err:
                if shared.verbose >= 2:
                    with shared.printLock:
                        print 'Could NOT connect to', peer, 'during outgoing attempt.', err

                timeLastSeen = shared.knownNodes[self.streamNumber][peer]
                if (int(time.time()) - timeLastSeen) > 172800 and len(
                        shared.knownNodes[self.streamNumber]
                ) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the shared.knownNodes data-structure.
                    shared.knownNodesLock.acquire()
                    del shared.knownNodes[self.streamNumber][peer]
                    shared.knownNodesLock.release()
                    with shared.printLock:
                        print 'deleting ', peer, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.'

            except socks.Socks5AuthError as err:
                shared.UISignalQueue.put(
                    ('updateStatusBar',
                     tr.translateText("MainWindow",
                                      "SOCKS5 Authentication problem: %1").arg(
                                          str(err))))
            except socks.Socks5Error as err:
                pass
                print 'SOCKS5 error. (It is possible that the server wants authentication).)', str(
                    err)
            except socks.Socks4Error as err:
                print 'Socks4Error:', err
            except socket.error as err:
                if shared.config.get('bitmessagesettings',
                                     'socksproxytype')[0:5] == 'SOCKS':
                    print 'Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str(
                        err)
                else:
                    if shared.verbose >= 1:
                        with shared.printLock:
                            print 'Could NOT connect to', peer, 'during outgoing attempt.', err

                    timeLastSeen = shared.knownNodes[self.streamNumber][peer]
                    if (int(time.time()) - timeLastSeen) > 172800 and len(
                            shared.knownNodes[self.streamNumber]
                    ) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the knownNodes data-structure.
                        shared.knownNodesLock.acquire()
                        del shared.knownNodes[self.streamNumber][peer]
                        shared.knownNodesLock.release()
                        with shared.printLock:
                            print 'deleting ', peer, 'from knownNodes because it is more than 48 hours old and we could not connect to it.'

            except Exception as err:
                sys.stderr.write(
                    'An exception has occurred in the outgoingSynSender thread that was not caught by other exception types: '
                )
                import traceback
                traceback.print_exc()
            time.sleep(0.1)
    def run(self):        
        self.conn = sqlite3.connect(shared.appdata + 'messages.dat')
        self.conn.text_factory = str
        self.cur = self.conn.cursor()

        try:
            self.cur.execute(
                '''CREATE TABLE inbox (msgid blob, toaddress text, fromaddress text, subject text, received text, message text, folder text, encodingtype int, read bool, UNIQUE(msgid) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''CREATE TABLE sent (msgid blob, toaddress text, toripe blob, fromaddress text, subject text, message text, ackdata blob, lastactiontime integer, status text, pubkeyretrynumber integer, msgretrynumber integer, folder text, encodingtype int)''' )
            self.cur.execute(
                '''CREATE TABLE subscriptions (label text, address text, enabled bool)''' )
            self.cur.execute(
                '''CREATE TABLE addressbook (label text, address text)''' )
            self.cur.execute(
                '''CREATE TABLE blacklist (label text, address text, enabled bool)''' )
            self.cur.execute(
                '''CREATE TABLE whitelist (label text, address text, enabled bool)''' )
            # Explanation of what is in the pubkeys table:
            #   The hash is the RIPEMD160 hash that is encoded in the Bitmessage address.
            #   transmitdata is literally the data that was included in the Bitmessage pubkey message when it arrived, except for the 24 byte protocol header- ie, it starts with the POW nonce.
            #   time is the time that the pubkey was broadcast on the network same as with every other type of Bitmessage object.
            # usedpersonally is set to "yes" if we have used the key
            # personally. This keeps us from deleting it because we may want to
            # reply to a message in the future. This field is not a bool
            # because we may need more flexability in the future and it doesn't
            # take up much more space anyway.
            self.cur.execute(
                '''CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''')
            self.cur.execute(
                '''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' )
            self.cur.execute( '''INSERT INTO settings VALUES('version','6')''')
            self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', (
                int(time.time()),))
            self.cur.execute(
                '''CREATE TABLE objectprocessorqueue (objecttype text, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)''' )
            self.conn.commit()
            logger.info('Created messages database file')
        except Exception as err:
            if str(err) == 'table inbox already exists':
                logger.debug('Database file already exists.')

            else:
                sys.stderr.write(
                    'ERROR trying to create database file (message.dat). Error message: %s\n' % str(err))
                os._exit(0)

        if shared.config.getint('bitmessagesettings', 'settingsversion') == 1:
            shared.config.set('bitmessagesettings', 'settingsversion', '2')
                      # If the settings version is equal to 2 or 3 then the
                      # sqlThread will modify the pubkeys table and change
                      # the settings version to 4.
            shared.config.set('bitmessagesettings', 'socksproxytype', 'none')
            shared.config.set('bitmessagesettings', 'sockshostname', 'localhost')
            shared.config.set('bitmessagesettings', 'socksport', '9050')
            shared.config.set('bitmessagesettings', 'socksauthentication', 'false')
            shared.config.set('bitmessagesettings', 'socksusername', '')
            shared.config.set('bitmessagesettings', 'sockspassword', '')
            shared.config.set('bitmessagesettings', 'sockslisten', 'false')
            shared.config.set('bitmessagesettings', 'keysencrypted', 'false')
            shared.config.set('bitmessagesettings', 'messagesencrypted', 'false')
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        # People running earlier versions of PyBitmessage do not have the
        # usedpersonally field in their pubkeys table. Let's add it.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 2:
            item = '''ALTER TABLE pubkeys ADD usedpersonally text DEFAULT 'no' '''
            parameters = ''
            self.cur.execute(item, parameters)
            self.conn.commit()

            shared.config.set('bitmessagesettings', 'settingsversion', '3')
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        # People running earlier versions of PyBitmessage do not have the
        # encodingtype field in their inbox and sent tables or the read field
        # in the inbox table. Let's add them.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 3:
            item = '''ALTER TABLE inbox ADD encodingtype int DEFAULT '2' '''
            parameters = ''
            self.cur.execute(item, parameters)

            item = '''ALTER TABLE inbox ADD read bool DEFAULT '1' '''
            parameters = ''
            self.cur.execute(item, parameters)

            item = '''ALTER TABLE sent ADD encodingtype int DEFAULT '2' '''
            parameters = ''
            self.cur.execute(item, parameters)
            self.conn.commit()

            shared.config.set('bitmessagesettings', 'settingsversion', '4')
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        if shared.config.getint('bitmessagesettings', 'settingsversion') == 4:
            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)

        # From now on, let us keep a 'version' embedded in the messages.dat
        # file so that when we make changes to the database, the database
        # version we are on can stay embedded in the messages.dat file. Let us
        # check to see if the settings table exists yet.
        item = '''SELECT name FROM sqlite_master WHERE type='table' AND name='settings';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if self.cur.fetchall() == []:
            # The settings table doesn't exist. We need to make it.
            logger.debug('In messages.dat database, creating new \'settings\' table.')
            self.cur.execute(
                '''CREATE TABLE settings (key text, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' )
            self.cur.execute( '''INSERT INTO settings VALUES('version','1')''')
            self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', (
                int(time.time()),))
            logger.debug('In messages.dat database, removing an obsolete field from the pubkeys table.')
            self.cur.execute(
                '''CREATE TEMPORARY TABLE pubkeys_backup(hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE);''')
            self.cur.execute(
                '''INSERT INTO pubkeys_backup SELECT hash, transmitdata, time, usedpersonally FROM pubkeys;''')
            self.cur.execute( '''DROP TABLE pubkeys''')
            self.cur.execute(
                '''CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''INSERT INTO pubkeys SELECT hash, transmitdata, time, usedpersonally FROM pubkeys_backup;''')
            self.cur.execute( '''DROP TABLE pubkeys_backup;''')
            logger.debug('Deleting all pubkeys from inventory. They will be redownloaded and then saved with the correct times.')
            self.cur.execute(
                '''delete from inventory where objecttype = 'pubkey';''')
            logger.debug('replacing Bitmessage announcements mailing list with a new one.')
            self.cur.execute(
                '''delete from subscriptions where address='BM-BbkPSZbzPwpVcYZpU4yHwf9ZPEapN5Zx' ''')
            self.cur.execute(
                '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''')
            logger.debug('Commiting.')
            self.conn.commit()
            logger.debug('Vacuuming message.dat. You might notice that the file size gets much smaller.')
            self.cur.execute( ''' VACUUM ''')

        # After code refactoring, the possible status values for sent messages
        # have changed.
        self.cur.execute(
            '''update sent set status='doingmsgpow' where status='doingpow'  ''')
        self.cur.execute(
            '''update sent set status='msgsent' where status='sentmessage'  ''')
        self.cur.execute(
            '''update sent set status='doingpubkeypow' where status='findingpubkey'  ''')
        self.cur.execute(
            '''update sent set status='broadcastqueued' where status='broadcastpending'  ''')
        self.conn.commit()
        
        if not shared.config.has_option('bitmessagesettings', 'sockslisten'):
            shared.config.set('bitmessagesettings', 'sockslisten', 'false')
            
        ensureNamecoinOptions()
            
        """# Add a new column to the inventory table to store the first 20 bytes of encrypted messages to support Android app
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if int(self.cur.fetchall()[0][0]) == 1:
            print 'upgrading database'
            item = '''ALTER TABLE inventory ADD first20bytesofencryptedmessage blob DEFAULT '' '''
            parameters = ''
            self.cur.execute(item, parameters)
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (2,)
            self.cur.execute(item, parameters)"""

        # Let's get rid of the first20bytesofencryptedmessage field in the inventory table.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if int(self.cur.fetchall()[0][0]) == 2:
            logger.debug('In messages.dat database, removing an obsolete field from the inventory table.')
            self.cur.execute(
                '''CREATE TEMPORARY TABLE inventory_backup(hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE);''')
            self.cur.execute(
                '''INSERT INTO inventory_backup SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory;''')
            self.cur.execute( '''DROP TABLE inventory''')
            self.cur.execute(
                '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''INSERT INTO inventory SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory_backup;''')
            self.cur.execute( '''DROP TABLE inventory_backup;''')
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (3,)
            self.cur.execute(item, parameters)

        # Add a new column to the inventory table to store tags.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 1 or currentVersion == 3:
            logger.debug('In messages.dat database, adding tag field to the inventory table.')
            item = '''ALTER TABLE inventory ADD tag blob DEFAULT '' '''
            parameters = ''
            self.cur.execute(item, parameters)
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (4,)
            self.cur.execute(item, parameters)

        if not shared.config.has_option('bitmessagesettings', 'userlocale'):
            shared.config.set('bitmessagesettings', 'userlocale', 'system')
        if not shared.config.has_option('bitmessagesettings', 'sendoutgoingconnections'):
            shared.config.set('bitmessagesettings', 'sendoutgoingconnections', 'True')

        # Raise the default required difficulty from 1 to 2
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 6:
            if int(shared.config.get('bitmessagesettings','defaultnoncetrialsperbyte')) == shared.networkDefaultProofOfWorkNonceTrialsPerByte:
                shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte', str(shared.networkDefaultProofOfWorkNonceTrialsPerByte * 2))
            shared.config.set('bitmessagesettings', 'settingsversion', '7')
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        # Add a new column to the pubkeys table to store the address version.
        # We're going to trash all of our pubkeys and let them be redownloaded.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 4:
            self.cur.execute( '''DROP TABLE pubkeys''')
            self.cur.execute(
                '''CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''delete from inventory where objecttype = 'pubkey';''')
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (5,)
            self.cur.execute(item, parameters)
            
        if not shared.config.has_option('bitmessagesettings', 'useidenticons'):
            shared.config.set('bitmessagesettings', 'useidenticons', 'True')
        if not shared.config.has_option('bitmessagesettings', 'identiconsuffix'): # acts as a salt
            shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons
            # Since we've added a new config entry, let's write keys.dat to disk.
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        #Adjusting time period to stop sending messages
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 7:
            shared.config.set(
                'bitmessagesettings', 'stopresendingafterxdays', '')
            shared.config.set(
                'bitmessagesettings', 'stopresendingafterxmonths', '')
            #shared.config.set(
            shared.config.set('bitmessagesettings', 'settingsversion', '8') 
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        # Add a new table: objectprocessorqueue with which to hold objects
        # that have yet to be processed if the user shuts down Bitmessage.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 5:
            self.cur.execute( '''DROP TABLE knownnodes''')
            self.cur.execute(
                '''CREATE TABLE objectprocessorqueue (objecttype text, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)''' )
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (6,)
            self.cur.execute(item, parameters)

        # Are you hoping to add a new option to the keys.dat file of existing
        # Bitmessage users? Add it right above this line!
        
        try:
            testpayload = '\x00\x00'
            t = ('1234', 1, testpayload, '12345678', 'no')
            self.cur.execute( '''INSERT INTO pubkeys VALUES(?,?,?,?,?)''', t)
            self.conn.commit()
            self.cur.execute(
                '''SELECT transmitdata FROM pubkeys WHERE hash='1234' ''')
            queryreturn = self.cur.fetchall()
            for row in queryreturn:
                transmitdata, = row
            self.cur.execute('''DELETE FROM pubkeys WHERE hash='1234' ''')
            self.conn.commit()
            if transmitdata == '':
                logger.fatal('Problem: The version of SQLite you have cannot store Null values. Please download and install the latest revision of your version of Python (for example, the latest Python 2.7 revision) and try again.\n')
                logger.fatal('PyBitmessage will now exit very abruptly. You may now see threading errors related to this abrupt exit but the problem you need to solve is related to SQLite.\n\n')
                os._exit(0)
        except Exception as err:
            if str(err) == 'database or disk is full':
                logger.fatal('(While null value test) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                if shared.daemon:
                    os._exit(0)
                else:
                    return
            else:
                logger.error(err)

        # Let us check to see the last time we vaccumed the messages.dat file.
        # If it has been more than a month let's do it now.
        item = '''SELECT value FROM settings WHERE key='lastvacuumtime';'''
        parameters = ''
        self.cur.execute(item, parameters)
        queryreturn = self.cur.fetchall()
        for row in queryreturn:
            value, = row
            if int(value) < int(time.time()) - 2592000:
                logger.info('It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...')
                try:
                    self.cur.execute( ''' VACUUM ''')
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(While VACUUM) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                item = '''update settings set value=? WHERE key='lastvacuumtime';'''
                parameters = (int(time.time()),)
                self.cur.execute(item, parameters)

        while True:
            item = shared.sqlSubmitQueue.get()
            if item == 'commit':
                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(While committing) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
            elif item == 'exit':
                self.conn.close()
                logger.info('sqlThread exiting gracefully.')

                return
            elif item == 'movemessagstoprog':
                logger.debug('the sqlThread is moving the messages.dat file to the local program directory.')

                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(while movemessagstoprog) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                self.conn.close()
                shutil.move(
                    shared.lookupAppdataFolder() + 'messages.dat', 'messages.dat')
                self.conn = sqlite3.connect('messages.dat')
                self.conn.text_factory = str
                self.cur = self.conn.cursor()
            elif item == 'movemessagstoappdata':
                logger.debug('the sqlThread is moving the messages.dat file to the Appdata folder.')

                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(while movemessagstoappdata) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                self.conn.close()
                shutil.move(
                    'messages.dat', shared.lookupAppdataFolder() + 'messages.dat')
                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()
                try:
                    self.cur.execute( ''' VACUUM ''')
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(while deleteandvacuume) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
            else:
                parameters = shared.sqlSubmitQueue.get()
                # print 'item', item
                # print 'parameters', parameters
                try:
                    self.cur.execute(item, parameters)
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(while cur.execute) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                    else:
                        logger.fatal('Major error occurred when trying to execute a SQL statement within the sqlThread. Please tell Atheros about this error message or post it in the forum! Error occurred while trying to execute statement: "%s"  Here are the parameters; you might want to censor this data with asterisks (***) as it can contain private information: %s. Here is the actual error message thrown by the sqlThread: %s', str(item), str(repr(parameters)),  str(err))
                        logger.fatal('This program shall now abruptly exit!')

                    os._exit(0)

                shared.sqlReturnQueue.put(self.cur.fetchall())
    def run(self):
        timeWeLastClearedInventoryAndPubkeysTables = 0

        while True:
            shared.UISignalQueue.put(
                ('updateStatusBar',
                 'Doing housekeeping (Flushing inventory in memory to disk...)'
                 ))

            with shared.inventoryLock:  # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock.
                with SqlBulkExecute() as sql:
                    for hash, storedValue in shared.inventory.items():
                        objectType, streamNumber, payload, receivedTime, tag = storedValue
                        if int(time.time()) - 3600 > receivedTime:
                            sql.execute(
                                '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''',
                                hash, objectType, streamNumber, payload,
                                receivedTime, tag)
                            del shared.inventory[hash]
            shared.UISignalQueue.put(('updateStatusBar', ''))
            shared.broadcastToSendDataQueues(
                (0, 'pong', 'no data')
            )  # commands the sendData threads to send out a pong message if they haven't sent anything else in the last five minutes. The socket timeout-time is 10 minutes.
            # If we are running as a daemon then we are going to fill up the UI
            # queue which will never be handled by a UI. We should clear it to
            # save memory.
            if shared.safeConfigGetBoolean('bitmessagesettings', 'daemon'):
                shared.UISignalQueue.queue.clear()
            if timeWeLastClearedInventoryAndPubkeysTables < int(
                    time.time()) - 7380:
                timeWeLastClearedInventoryAndPubkeysTables = int(time.time())
                # inventory (moves data from the inventory data structure to
                # the on-disk sql database)
                # inventory (clears pubkeys after 28 days and everything else
                # after 2 days and 12 hours)
                sqlExecute(
                    '''DELETE FROM inventory WHERE (receivedtime<? AND objecttype<>'pubkey') OR (receivedtime<?  AND objecttype='pubkey') ''',
                    int(time.time()) -
                    shared.lengthOfTimeToLeaveObjectsInInventory,
                    int(time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys)

                # pubkeys
                sqlExecute(
                    '''DELETE FROM pubkeys WHERE time<? AND usedpersonally='no' ''',
                    int(time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys)

                queryreturn = sqlQuery(
                    '''select toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, status, pubkeyretrynumber, msgretrynumber FROM sent WHERE ((status='awaitingpubkey' OR status='msgsent') AND folder='sent') '''
                )  # If the message's folder='trash' then we'll ignore it.
                for row in queryreturn:
                    if len(row) < 5:
                        with shared.printLock:
                            sys.stderr.write(
                                'Something went wrong in the singleCleaner thread: a query did not return the requested fields. '
                                + repr(row))
                        time.sleep(3)

                        break
                    toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, status, pubkeyretrynumber, msgretrynumber = row
                    if status == 'awaitingpubkey':
                        if int(time.time()) - lastactiontime > (
                                shared.
                                maximumAgeOfAnObjectThatIAmWillingToAccept *
                            (2**(pubkeyretrynumber))):
                            print 'It has been a long time and we haven\'t heard a response to our getpubkey request. Sending again.'
                            try:
                                del shared.neededPubkeys[
                                    toripe]  # We need to take this entry out of the shared.neededPubkeys structure because the shared.workerQueue checks to see whether the entry is already present and will not do the POW and send the message because it assumes that it has already done it recently.
                            except:
                                pass

                            shared.UISignalQueue.put((
                                'updateStatusBar',
                                'Doing work necessary to again attempt to request a public key...'
                            ))
                            t = ()
                            sqlExecute(
                                '''UPDATE sent SET lastactiontime=?, pubkeyretrynumber=?, status='msgqueued' WHERE toripe=?''',
                                int(time.time()), pubkeyretrynumber + 1,
                                toripe)
                            shared.workerQueue.put(('sendmessage', ''))
                    else:  # status == msgsent
                        if int(time.time()) - lastactiontime > (
                                shared.
                                maximumAgeOfAnObjectThatIAmWillingToAccept *
                            (2**(msgretrynumber))):
                            print 'It has been a long time and we haven\'t heard an acknowledgement to our msg. Sending again.'
                            sqlExecute(
                                '''UPDATE sent SET lastactiontime=?, msgretrynumber=?, status=? WHERE ackdata=?''',
                                int(time.time()), msgretrynumber + 1,
                                'msgqueued', ackdata)
                            shared.workerQueue.put(('sendmessage', ''))
                            shared.UISignalQueue.put((
                                'updateStatusBar',
                                'Doing work necessary to again attempt to deliver a message...'
                            ))

                # Let's also clear and reload shared.inventorySets to keep it from
                # taking up an unnecessary amount of memory.
                for streamNumber in shared.inventorySets:
                    shared.inventorySets[streamNumber] = set()
                    queryData = sqlQuery(
                        '''SELECT hash FROM inventory WHERE streamnumber=?''',
                        streamNumber)
                    for row in queryData:
                        shared.inventorySets[streamNumber].add(row[0])
                with shared.inventoryLock:
                    for hash, storedValue in shared.inventory.items():
                        objectType, streamNumber, payload, receivedTime, tag = storedValue
                        if streamNumber in shared.inventorySets:
                            shared.inventorySets[streamNumber].add(hash)

            # Let us write out the knowNodes to disk if there is anything new to write out.
            if shared.needToWriteKnownNodesToDisk:
                shared.knownNodesLock.acquire()
                output = open(shared.appdata + 'knownnodes.dat', 'wb')
                try:
                    pickle.dump(shared.knownNodes, output)
                    output.close()
                except Exception as err:
                    if "Errno 28" in str(err):
                        logger.fatal(
                            '(while receiveDataThread shared.needToWriteKnownNodesToDisk) Alert: Your disk or data storage volume is full. '
                        )
                        shared.UISignalQueue.put(('alert', (
                            tr.translateText("MainWindow", "Disk full"),
                            tr.translateText(
                                "MainWindow",
                                'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'
                            ), True)))
                        if shared.daemon:
                            os._exit(0)
                shared.knownNodesLock.release()
                shared.needToWriteKnownNodesToDisk = False
            time.sleep(300)
    def processmsg(self, data):
        messageProcessingStartTime = time.time()
        shared.numberOfMessagesProcessed += 1
        shared.UISignalQueue.put((
            'updateNumberOfMessagesProcessed', 'no data'))
        readPosition = 20 # bypass the nonce, time, and object type
        msgVersion, msgVersionLength = decodeVarint(data[readPosition:readPosition + 9])
        if msgVersion != 1:
            logger.info('Cannot understand message versions other than one. Ignoring message.') 
            return
        readPosition += msgVersionLength
        
        streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = decodeVarint(
            data[readPosition:readPosition + 9])
        readPosition += streamNumberAsClaimedByMsgLength
        inventoryHash = calculateInventoryHash(data)
        initialDecryptionSuccessful = False
        # Let's check whether this is a message acknowledgement bound for us.
        if data[-32:] in shared.ackdataForWhichImWatching:
            logger.info('This msg IS an acknowledgement bound for me.')
            del shared.ackdataForWhichImWatching[data[-32:]]
            sqlExecute('UPDATE sent SET status=?, lastactiontime=? WHERE ackdata=?',
                       'ackreceived',
                       int(time.time()), 
                       data[-32:])
            shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (data[-32:], tr.translateText("MainWindow",'Acknowledgement of the message received. %1').arg(l10n.formatTimestamp()))))
            return
        else:
            logger.info('This was NOT an acknowledgement bound for me.')


        # This is not an acknowledgement bound for me. See if it is a message
        # bound for me by trying to decrypt it with my private keys.
        
        for key, cryptorObject in shared.myECCryptorObjects.items():
            try:
                decryptedData = cryptorObject.decrypt(data[readPosition:])
                toRipe = key  # This is the RIPE hash of my pubkeys. We need this below to compare to the destination_ripe included in the encrypted data.
                initialDecryptionSuccessful = True
                logger.info('EC decryption successful using key associated with ripe hash: %s.' % key.encode('hex'))
                break
            except Exception as err:
                pass
        if not initialDecryptionSuccessful:
            # This is not a message bound for me.
            logger.info('Length of time program spent failing to decrypt this message: %s seconds.' % (time.time() - messageProcessingStartTime,)) 
            return

        # This is a message bound for me.
        toAddress = shared.myAddressesByHash[
            toRipe]  # Look up my address based on the RIPE hash.
        readPosition = 0
        sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += sendersAddressVersionNumberLength
        if sendersAddressVersionNumber == 0:
            logger.info('Cannot understand sendersAddressVersionNumber = 0. Ignoring message.') 
            return
        if sendersAddressVersionNumber > 4:
            logger.info('Sender\'s address version number %s not yet supported. Ignoring message.' % sendersAddressVersionNumber)  
            return
        if len(decryptedData) < 170:
            logger.info('Length of the unencrypted data is unreasonably short. Sanity check failed. Ignoring message.')
            return
        sendersStreamNumber, sendersStreamNumberLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        if sendersStreamNumber == 0:
            logger.info('sender\'s stream number is 0. Ignoring message.')
            return
        readPosition += sendersStreamNumberLength
        behaviorBitfield = decryptedData[readPosition:readPosition + 4]
        readPosition += 4
        pubSigningKey = '\x04' + decryptedData[
            readPosition:readPosition + 64]
        readPosition += 64
        pubEncryptionKey = '\x04' + decryptedData[
            readPosition:readPosition + 64]
        readPosition += 64
        if sendersAddressVersionNumber >= 3:
            requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(
                decryptedData[readPosition:readPosition + 10])
            readPosition += varintLength
            logger.info('sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s' % requiredAverageProofOfWorkNonceTrialsPerByte)
            requiredPayloadLengthExtraBytes, varintLength = decodeVarint(
                decryptedData[readPosition:readPosition + 10])
            readPosition += varintLength
            logger.info('sender\'s requiredPayloadLengthExtraBytes is %s' % requiredPayloadLengthExtraBytes)
        endOfThePublicKeyPosition = readPosition  # needed for when we store the pubkey in our database of pubkeys for later use.
        if toRipe != decryptedData[readPosition:readPosition + 20]:
            logger.info('The original sender of this message did not send it to you. Someone is attempting a Surreptitious Forwarding Attack.\n\
                See: http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html \n\
                your toRipe: %s\n\
                embedded destination toRipe: %s' % (toRipe.encode('hex'), decryptedData[readPosition:readPosition + 20].encode('hex'))
                       )
            return
        readPosition += 20
        messageEncodingType, messageEncodingTypeLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += messageEncodingTypeLength
        messageLength, messageLengthLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += messageLengthLength
        message = decryptedData[readPosition:readPosition + messageLength]
        # print 'First 150 characters of message:', repr(message[:150])
        readPosition += messageLength
        ackLength, ackLengthLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += ackLengthLength
        ackData = decryptedData[readPosition:readPosition + ackLength]
        readPosition += ackLength
        positionOfBottomOfAckData = readPosition  # needed to mark the end of what is covered by the signature
        signatureLength, signatureLengthLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += signatureLengthLength
        signature = decryptedData[
            readPosition:readPosition + signatureLength]
        signedData = data[8:20] + encodeVarint(1) + encodeVarint(streamNumberAsClaimedByMsg) + decryptedData[:positionOfBottomOfAckData]
        
        if not highlevelcrypto.verify(signedData, signature, pubSigningKey.encode('hex')):
            logger.debug('ECDSA verify failed')
            return
        logger.debug('ECDSA verify passed')
        logger.debug('As a matter of intellectual curiosity, here is the Bitcoin address associated with the keys owned by the other person: %s  ..and here is the testnet address: %s. The other person must take their private signing key from Bitmessage and import it into Bitcoin (or a service like Blockchain.info) for it to be of any use. Do not use this unless you know what you are doing.' %
                     (helper_bitcoin.calculateBitcoinAddressFromPubkey(pubSigningKey), helper_bitcoin.calculateTestnetAddressFromPubkey(pubSigningKey))
                     )
        sigHash = hashlib.sha512(hashlib.sha512(signature).digest()).digest()[32:] # Used to detect and ignore duplicate messages in our inbox

        # calculate the fromRipe.
        sha = hashlib.new('sha512')
        sha.update(pubSigningKey + pubEncryptionKey)
        ripe = hashlib.new('ripemd160')
        ripe.update(sha.digest())
        fromAddress = encodeAddress(
            sendersAddressVersionNumber, sendersStreamNumber, ripe.digest())
        
        # Let's store the public key in case we want to reply to this
        # person.
        sqlExecute(
            '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
            fromAddress,
            sendersAddressVersionNumber,
            decryptedData[:endOfThePublicKeyPosition],
            int(time.time()),
            'yes')
        
        # 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(fromAddress)
        
        # If this message is bound for one of my version 3 addresses (or
        # higher), then we must check to make sure it meets our demanded
        # proof of work requirement. If this is bound for one of my chan
        # addresses then we skip this check; the minimum network POW is
        # fine.
        if decodeAddress(toAddress)[1] >= 3 and not shared.safeConfigGetBoolean(toAddress, 'chan'):  # If the toAddress version number is 3 or higher and not one of my chan addresses:
            if not shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(fromAddress):  # If I'm not friendly with this person:
                requiredNonceTrialsPerByte = shared.config.getint(
                    toAddress, 'noncetrialsperbyte')
                requiredPayloadLengthExtraBytes = shared.config.getint(
                    toAddress, 'payloadlengthextrabytes')
                if not shared.isProofOfWorkSufficient(data, requiredNonceTrialsPerByte, requiredPayloadLengthExtraBytes):
                    logger.info('Proof of work in msg is insufficient only because it does not meet our higher requirement.')
                    return
        blockMessage = False  # Gets set to True if the user shouldn't see the message according to black or white lists.
        if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black':  # If we are using a blacklist
            queryreturn = sqlQuery(
                '''SELECT label FROM blacklist where address=? and enabled='1' ''',
                fromAddress)
            if queryreturn != []:
                logger.info('Message ignored because address is in blacklist.')

                blockMessage = True
        else:  # We're using a whitelist
            queryreturn = sqlQuery(
                '''SELECT label FROM whitelist where address=? and enabled='1' ''',
                fromAddress)
            if queryreturn == []:
                logger.info('Message ignored because address not in whitelist.')
                blockMessage = True
        
        toLabel = shared.config.get(toAddress, 'label')
        if toLabel == '':
            toLabel = toAddress

        if messageEncodingType == 2:
            subject, body = self.decodeType2Message(message)
            logger.info('Message subject (first 100 characters): %s' % repr(subject)[:100])
        elif messageEncodingType == 1:
            body = message
            subject = ''
        elif messageEncodingType == 0:
            logger.info('messageEncodingType == 0. Doing nothing with the message. They probably just sent it so that we would store their public key or send their ack data for them.')
            subject = ''
            body = '' 
        else:
            body = 'Unknown encoding type.\n\n' + repr(message)
            subject = ''
        # Let us make sure that we haven't already received this message
        if helper_inbox.isMessageAlreadyInInbox(sigHash):
            logger.info('This msg is already in our inbox. Ignoring it.')
            blockMessage = True
        if not blockMessage:
            if messageEncodingType != 0:
                t = (inventoryHash, toAddress, fromAddress, subject, int(
                    time.time()), body, 'inbox', messageEncodingType, 0, sigHash)
                helper_inbox.insert(t)

                shared.UISignalQueue.put(('displayNewInboxMessage', (
                    inventoryHash, toAddress, fromAddress, subject, body)))

            # If we are behaving as an API then we might need to run an
            # outside command to let some program know that a new message
            # has arrived.
            if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'):
                try:
                    apiNotifyPath = shared.config.get(
                        'bitmessagesettings', 'apinotifypath')
                except:
                    apiNotifyPath = ''
                if apiNotifyPath != '':
                    call([apiNotifyPath, "newMessage"])

            # Let us now check and see whether our receiving address is
            # behaving as a mailing list
            if shared.safeConfigGetBoolean(toAddress, 'mailinglist'):
                try:
                    mailingListName = shared.config.get(
                        toAddress, 'mailinglistname')
                except:
                    mailingListName = ''
                # Let us send out this message as a broadcast
                subject = self.addMailingListNameToSubject(
                    subject, mailingListName)
                # Let us now send this message out as a broadcast
                message = time.strftime("%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime(
                )) + '   Message ostensibly from ' + fromAddress + ':\n\n' + body
                fromAddress = toAddress  # The fromAddress for the broadcast that we are about to send is the toAddress (my address) for the msg message we are currently processing.
                ackdataForBroadcast = OpenSSL.rand(
                    32)  # We don't actually need the ackdataForBroadcast for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating.
                toAddress = '[Broadcast subscribers]'
                ripe = ''

                # We really should have a discussion about how to
                # set the TTL for mailing list broadcasts. This is obviously
                # hard-coded. 
                TTL = 2*7*24*60*60 # 2 weeks
                t = ('', 
                     toAddress, 
                     ripe, 
                     fromAddress, 
                     subject, 
                     message, 
                     ackdataForBroadcast, 
                     int(time.time()), # sentTime (this doesn't change)
                     int(time.time()), # lastActionTime
                     0, 
                     'broadcastqueued', 
                     0, 
                     'sent', 
                     2, 
                     TTL)
                helper_sent.insert(t)

                shared.UISignalQueue.put(('displayNewSentMessage', (
                    toAddress, '[Broadcast subscribers]', fromAddress, subject, message, ackdataForBroadcast)))
                shared.workerQueue.put(('sendbroadcast', ''))

        if self.ackDataHasAVaildHeader(ackData):
            shared.checkAndShareObjectWithPeers(ackData[24:])

        # Display timing data
        timeRequiredToAttemptToDecryptMessage = time.time(
        ) - messageProcessingStartTime
        shared.successfullyDecryptMessageTimings.append(
            timeRequiredToAttemptToDecryptMessage)
        sum = 0
        for item in shared.successfullyDecryptMessageTimings:
            sum += item
        logger.debug('Time to decrypt this message successfully: %s\n\
                     Average time for all message decryption successes since startup: %s.' %
                     (timeRequiredToAttemptToDecryptMessage, sum / len(shared.successfullyDecryptMessageTimings)) 
                     )
    def run(self):
        self.conn = sqlite3.connect(shared.appdata + "messages.dat")
        self.conn.text_factory = str
        self.cur = self.conn.cursor()

        try:
            self.cur.execute(
                """CREATE TABLE inbox (msgid blob, toaddress text, fromaddress text, subject text, received text, message text, folder text, encodingtype int, read bool, UNIQUE(msgid) ON CONFLICT REPLACE)"""
            )
            self.cur.execute(
                """CREATE TABLE sent (msgid blob, toaddress text, toripe blob, fromaddress text, subject text, message text, ackdata blob, lastactiontime integer, status text, pubkeyretrynumber integer, msgretrynumber integer, folder text, encodingtype int)"""
            )
            self.cur.execute("""CREATE TABLE subscriptions (label text, address text, enabled bool)""")
            self.cur.execute("""CREATE TABLE addressbook (label text, address text)""")
            self.cur.execute("""CREATE TABLE blacklist (label text, address text, enabled bool)""")
            self.cur.execute("""CREATE TABLE whitelist (label text, address text, enabled bool)""")
            """
            Explanation of what is in the pubkeys table:
                The hash is the RIPEMD160 hash that is encoded in the Bitmessage address.
              
                transmitdata /was/ literally the data that was included in the Bitmessage pubkey message when it arrived, 
                except for the 24 byte protocol header- ie, it started with the POW nonce. Since protocol v3, to maintain
                backwards compability, the data format of the data on disk is staying the same even though the wire format has changed.
              
                time is the time that the pubkey was broadcast on the network same as with every other type of Bitmessage object.
              
                usedpersonally is set to "yes" if we have used the key personally. This keeps us from deleting it because we may want to
                reply to a message in the future. This field is not a bool because we may need more flexability in the future and it doesn't
                take up much more space anyway.
            """
            self.cur.execute(
                """CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)"""
            )
            self.cur.execute(
                """CREATE TABLE inventory (hash blob, objecttype int, streamnumber int, payload blob, expirestime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)"""
            )
            self.cur.execute(
                """INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)"""
            )
            self.cur.execute("""CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)""")
            self.cur.execute("""INSERT INTO settings VALUES('version','7')""")
            self.cur.execute("""INSERT INTO settings VALUES('lastvacuumtime',?)""", (int(time.time()),))
            self.cur.execute(
                """CREATE TABLE objectprocessorqueue (objecttype int, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)"""
            )
            self.conn.commit()
            logger.info("Created messages database file")
        except Exception as err:
            if str(err) == "table inbox already exists":
                logger.debug("Database file already exists.")

            else:
                sys.stderr.write("ERROR trying to create database file (message.dat). Error message: %s\n" % str(err))
                os._exit(0)

        try:
            self.cur.execute(
                """CREATE TABLE consensus (id INTEGER PRIMARY KEY AUTOINCREMENT, chanaddress text, hash text,
                                data BLOB, settings TEXT, UNIQUE(chanaddress))"""
            )
            self.cur.execute(
                """CREATE TABLE consensus_messages (consensus_id INTEGER, local_time BIGINT, message BLOB,
                                                                 message_hash BLOB, state INTEGER)"""
            )
            self.conn.commit()
        except Exception as err:
            if str(err) != "table consensus already exists":
                sys.stderr.write("ERROR trying to create consensus table. Error message: %s\n" % str(err))
                os._exit(0)

        if shared.config.getint("bitmessagesettings", "settingsversion") == 1:
            shared.config.set("bitmessagesettings", "settingsversion", "2")
            # If the settings version is equal to 2 or 3 then the
            # sqlThread will modify the pubkeys table and change
            # the settings version to 4.
            shared.config.set("bitmessagesettings", "socksproxytype", "none")
            shared.config.set("bitmessagesettings", "sockshostname", "localhost")
            shared.config.set("bitmessagesettings", "socksport", "9050")
            shared.config.set("bitmessagesettings", "socksauthentication", "false")
            shared.config.set("bitmessagesettings", "socksusername", "")
            shared.config.set("bitmessagesettings", "sockspassword", "")
            shared.config.set("bitmessagesettings", "sockslisten", "false")
            shared.config.set("bitmessagesettings", "keysencrypted", "false")
            shared.config.set("bitmessagesettings", "messagesencrypted", "false")
            with open(shared.appdata + "keys.dat", "wb") as configfile:
                shared.config.write(configfile)

        # People running earlier versions of PyBitmessage do not have the
        # usedpersonally field in their pubkeys table. Let's add it.
        if shared.config.getint("bitmessagesettings", "settingsversion") == 2:
            item = """ALTER TABLE pubkeys ADD usedpersonally text DEFAULT 'no' """
            parameters = ""
            self.cur.execute(item, parameters)
            self.conn.commit()

            shared.config.set("bitmessagesettings", "settingsversion", "3")
            with open(shared.appdata + "keys.dat", "wb") as configfile:
                shared.config.write(configfile)

        # People running earlier versions of PyBitmessage do not have the
        # encodingtype field in their inbox and sent tables or the read field
        # in the inbox table. Let's add them.
        if shared.config.getint("bitmessagesettings", "settingsversion") == 3:
            item = """ALTER TABLE inbox ADD encodingtype int DEFAULT '2' """
            parameters = ""
            self.cur.execute(item, parameters)

            item = """ALTER TABLE inbox ADD read bool DEFAULT '1' """
            parameters = ""
            self.cur.execute(item, parameters)

            item = """ALTER TABLE sent ADD encodingtype int DEFAULT '2' """
            parameters = ""
            self.cur.execute(item, parameters)
            self.conn.commit()

            shared.config.set("bitmessagesettings", "settingsversion", "4")

        if shared.config.getint("bitmessagesettings", "settingsversion") == 4:
            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")
        # From now on, let us keep a 'version' embedded in the messages.dat
        # file so that when we make changes to the database, the database
        # version we are on can stay embedded in the messages.dat file. Let us
        # check to see if the settings table exists yet.
        item = """SELECT name FROM sqlite_master WHERE type='table' AND name='settings';"""
        parameters = ""
        self.cur.execute(item, parameters)
        if self.cur.fetchall() == []:
            # The settings table doesn't exist. We need to make it.
            logger.debug("In messages.dat database, creating new 'settings' table.")
            self.cur.execute("""CREATE TABLE settings (key text, value blob, UNIQUE(key) ON CONFLICT REPLACE)""")
            self.cur.execute("""INSERT INTO settings VALUES('version','1')""")
            self.cur.execute("""INSERT INTO settings VALUES('lastvacuumtime',?)""", (int(time.time()),))
            logger.debug("In messages.dat database, removing an obsolete field from the pubkeys table.")
            self.cur.execute(
                """CREATE TEMPORARY TABLE pubkeys_backup(hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE);"""
            )
            self.cur.execute(
                """INSERT INTO pubkeys_backup SELECT hash, transmitdata, time, usedpersonally FROM pubkeys;"""
            )
            self.cur.execute("""DROP TABLE pubkeys""")
            self.cur.execute(
                """CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)"""
            )
            self.cur.execute(
                """INSERT INTO pubkeys SELECT hash, transmitdata, time, usedpersonally FROM pubkeys_backup;"""
            )
            self.cur.execute("""DROP TABLE pubkeys_backup;""")
            logger.debug(
                "Deleting all pubkeys from inventory. They will be redownloaded and then saved with the correct times."
            )
            self.cur.execute("""delete from inventory where objecttype = 'pubkey';""")
            logger.debug("replacing Bitmessage announcements mailing list with a new one.")
            self.cur.execute("""delete from subscriptions where address='BM-BbkPSZbzPwpVcYZpU4yHwf9ZPEapN5Zx' """)
            self.cur.execute(
                """INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)"""
            )
            logger.debug("Commiting.")
            self.conn.commit()
            logger.debug("Vacuuming message.dat. You might notice that the file size gets much smaller.")
            self.cur.execute(""" VACUUM """)

        # After code refactoring, the possible status values for sent messages
        # have changed.
        self.cur.execute("""update sent set status='doingmsgpow' where status='doingpow'  """)
        self.cur.execute("""update sent set status='msgsent' where status='sentmessage'  """)
        self.cur.execute("""update sent set status='doingpubkeypow' where status='findingpubkey'  """)
        self.cur.execute("""update sent set status='broadcastqueued' where status='broadcastpending'  """)
        self.conn.commit()

        if not shared.config.has_option("bitmessagesettings", "sockslisten"):
            shared.config.set("bitmessagesettings", "sockslisten", "false")

        ensureNamecoinOptions()

        """# Add a new column to the inventory table to store the first 20 bytes of encrypted messages to support Android app
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if int(self.cur.fetchall()[0][0]) == 1:
            print 'upgrading database'
            item = '''ALTER TABLE inventory ADD first20bytesofencryptedmessage blob DEFAULT '' '''
            parameters = ''
            self.cur.execute(item, parameters)
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (2,)
            self.cur.execute(item, parameters)"""

        # Let's get rid of the first20bytesofencryptedmessage field in the inventory table.
        item = """SELECT value FROM settings WHERE key='version';"""
        parameters = ""
        self.cur.execute(item, parameters)
        if int(self.cur.fetchall()[0][0]) == 2:
            logger.debug("In messages.dat database, removing an obsolete field from the inventory table.")
            self.cur.execute(
                """CREATE TEMPORARY TABLE inventory_backup(hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE);"""
            )
            self.cur.execute(
                """INSERT INTO inventory_backup SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory;"""
            )
            self.cur.execute("""DROP TABLE inventory""")
            self.cur.execute(
                """CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE)"""
            )
            self.cur.execute(
                """INSERT INTO inventory SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory_backup;"""
            )
            self.cur.execute("""DROP TABLE inventory_backup;""")
            item = """update settings set value=? WHERE key='version';"""
            parameters = (3,)
            self.cur.execute(item, parameters)

        # Add a new column to the inventory table to store tags.
        item = """SELECT value FROM settings WHERE key='version';"""
        parameters = ""
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 1 or currentVersion == 3:
            logger.debug("In messages.dat database, adding tag field to the inventory table.")
            item = """ALTER TABLE inventory ADD tag blob DEFAULT '' """
            parameters = ""
            self.cur.execute(item, parameters)
            item = """update settings set value=? WHERE key='version';"""
            parameters = (4,)
            self.cur.execute(item, parameters)

        if not shared.config.has_option("bitmessagesettings", "userlocale"):
            shared.config.set("bitmessagesettings", "userlocale", "system")
        if not shared.config.has_option("bitmessagesettings", "sendoutgoingconnections"):
            shared.config.set("bitmessagesettings", "sendoutgoingconnections", "True")

        # Raise the default required difficulty from 1 to 2
        # With the change to protocol v3, this is obsolete.
        if shared.config.getint("bitmessagesettings", "settingsversion") == 6:
            """if int(shared.config.get('bitmessagesettings','defaultnoncetrialsperbyte')) == shared.networkDefaultProofOfWorkNonceTrialsPerByte:
                shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte', str(shared.networkDefaultProofOfWorkNonceTrialsPerByte * 2))
                """
            shared.config.set("bitmessagesettings", "settingsversion", "7")

        # Add a new column to the pubkeys table to store the address version.
        # We're going to trash all of our pubkeys and let them be redownloaded.
        item = """SELECT value FROM settings WHERE key='version';"""
        parameters = ""
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 4:
            self.cur.execute("""DROP TABLE pubkeys""")
            self.cur.execute(
                """CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)"""
            )
            self.cur.execute("""delete from inventory where objecttype = 'pubkey';""")
            item = """update settings set value=? WHERE key='version';"""
            parameters = (5,)
            self.cur.execute(item, parameters)

        if not shared.config.has_option("bitmessagesettings", "useidenticons"):
            shared.config.set("bitmessagesettings", "useidenticons", "True")
        if not shared.config.has_option("bitmessagesettings", "identiconsuffix"):  # acts as a salt
            shared.config.set(
                "bitmessagesettings",
                "identiconsuffix",
                "".join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12)),
            )  # a twelve character pseudo-password to salt the identicons

        # Add settings to support no longer resending messages after a certain period of time even if we never get an ack
        if shared.config.getint("bitmessagesettings", "settingsversion") == 7:
            shared.config.set("bitmessagesettings", "stopresendingafterxdays", "")
            shared.config.set("bitmessagesettings", "stopresendingafterxmonths", "")
            # shared.config.set(
            shared.config.set("bitmessagesettings", "settingsversion", "8")
            with open(shared.appdata + "keys.dat", "wb") as configfile:
                shared.config.write(configfile)

        # Add a new table: objectprocessorqueue with which to hold objects
        # that have yet to be processed if the user shuts down Bitmessage.
        item = """SELECT value FROM settings WHERE key='version';"""
        parameters = ""
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 5:
            self.cur.execute("""DROP TABLE knownnodes""")
            self.cur.execute(
                """CREATE TABLE objectprocessorqueue (objecttype text, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)"""
            )
            item = """update settings set value=? WHERE key='version';"""
            parameters = (6,)
            self.cur.execute(item, parameters)

        # changes related to protocol v3
        #    In table inventory and objectprocessorqueue, objecttype is now an integer (it was a human-friendly string previously)
        item = """SELECT value FROM settings WHERE key='version';"""
        parameters = ""
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 6:
            logger.debug("In messages.dat database, dropping and recreating the inventory table.")
            self.cur.execute("""DROP TABLE inventory""")
            self.cur.execute(
                """CREATE TABLE inventory (hash blob, objecttype int, streamnumber int, payload blob, expirestime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)"""
            )
            self.cur.execute("""DROP TABLE objectprocessorqueue""")
            self.cur.execute(
                """CREATE TABLE objectprocessorqueue (objecttype int, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)"""
            )
            item = """update settings set value=? WHERE key='version';"""
            parameters = (7,)
            self.cur.execute(item, parameters)
            logger.debug("Finished dropping and recreating the inventory table.")

        # With the change to protocol version 3, reset the user-settable difficulties to 1
        if shared.config.getint("bitmessagesettings", "settingsversion") == 8:
            shared.config.set(
                "bitmessagesettings",
                "defaultnoncetrialsperbyte",
                str(shared.networkDefaultProofOfWorkNonceTrialsPerByte),
            )
            shared.config.set(
                "bitmessagesettings",
                "defaultpayloadlengthextrabytes",
                str(shared.networkDefaultPayloadLengthExtraBytes),
            )
            previousTotalDifficulty = (
                int(shared.config.getint("bitmessagesettings", "maxacceptablenoncetrialsperbyte")) / 320
            )
            previousSmallMessageDifficulty = (
                int(shared.config.getint("bitmessagesettings", "maxacceptablepayloadlengthextrabytes")) / 14000
            )
            shared.config.set(
                "bitmessagesettings", "maxacceptablenoncetrialsperbyte", str(previousTotalDifficulty * 1000)
            )
            shared.config.set(
                "bitmessagesettings", "maxacceptablepayloadlengthextrabytes", str(previousSmallMessageDifficulty * 1000)
            )
            shared.config.set("bitmessagesettings", "settingsversion", "9")

        # Adjust the required POW values for each of this user's addresses to conform to protocol v3 norms.
        if shared.config.getint("bitmessagesettings", "settingsversion") == 9:
            for addressInKeysFile in shared.config.sections():
                try:
                    previousTotalDifficulty = float(shared.config.getint(addressInKeysFile, "noncetrialsperbyte")) / 320
                    previousSmallMessageDifficulty = (
                        float(shared.config.getint(addressInKeysFile, "payloadlengthextrabytes")) / 14000
                    )
                    if previousTotalDifficulty <= 2:
                        previousTotalDifficulty = 1
                    if previousSmallMessageDifficulty < 1:
                        previousSmallMessageDifficulty = 1
                    shared.config.set(addressInKeysFile, "noncetrialsperbyte", str(int(previousTotalDifficulty * 1000)))
                    shared.config.set(
                        addressInKeysFile, "payloadlengthextrabytes", str(int(previousSmallMessageDifficulty * 1000))
                    )
                except:
                    continue
            shared.config.set("bitmessagesettings", "maxdownloadrate", "0")
            shared.config.set("bitmessagesettings", "maxuploadrate", "0")
            shared.config.set("bitmessagesettings", "settingsversion", "10")
            with open(shared.appdata + "keys.dat", "wb") as configfile:
                shared.config.write(configfile)

        # Are you hoping to add a new option to the keys.dat file of existing
        # Bitmessage users or modify the SQLite database? Add it right above this line!

        try:
            testpayload = "\x00\x00"
            t = ("1234", 1, testpayload, "12345678", "no")
            self.cur.execute("""INSERT INTO pubkeys VALUES(?,?,?,?,?)""", t)
            self.conn.commit()
            self.cur.execute("""SELECT transmitdata FROM pubkeys WHERE hash='1234' """)
            queryreturn = self.cur.fetchall()
            for row in queryreturn:
                transmitdata, = row
            self.cur.execute("""DELETE FROM pubkeys WHERE hash='1234' """)
            self.conn.commit()
            if transmitdata == "":
                logger.fatal(
                    "Problem: The version of SQLite you have cannot store Null values. Please download and install the latest revision of your version of Python (for example, the latest Python 2.7 revision) and try again.\n"
                )
                logger.fatal(
                    "PyBitmessage will now exit very abruptly. You may now see threading errors related to this abrupt exit but the problem you need to solve is related to SQLite.\n\n"
                )
                os._exit(0)
        except Exception as err:
            if str(err) == "database or disk is full":
                logger.fatal(
                    "(While null value test) Alert: Your disk or data storage volume is full. sqlThread will now exit."
                )
                shared.UISignalQueue.put(
                    (
                        "alert",
                        (
                            tr.translateText("MainWindow", "Disk full"),
                            tr.translateText(
                                "MainWindow",
                                "Alert: Your disk or data storage volume is full. Bitmessage will now exit.",
                            ),
                            True,
                        ),
                    )
                )
                if shared.daemon:
                    os._exit(0)
                else:
                    return
            else:
                logger.error(err)

        # Let us check to see the last time we vaccumed the messages.dat file.
        # If it has been more than a month let's do it now.
        item = """SELECT value FROM settings WHERE key='lastvacuumtime';"""
        parameters = ""
        self.cur.execute(item, parameters)
        queryreturn = self.cur.fetchall()
        for row in queryreturn:
            value, = row
            if int(value) < int(time.time()) - 2592000:
                logger.info("It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...")
                try:
                    self.cur.execute(""" VACUUM """)
                except Exception as err:
                    if str(err) == "database or disk is full":
                        logger.fatal(
                            "(While VACUUM) Alert: Your disk or data storage volume is full. sqlThread will now exit."
                        )
                        shared.UISignalQueue.put(
                            (
                                "alert",
                                (
                                    tr.translateText("MainWindow", "Disk full"),
                                    tr.translateText(
                                        "MainWindow",
                                        "Alert: Your disk or data storage volume is full. Bitmessage will now exit.",
                                    ),
                                    True,
                                ),
                            )
                        )
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                item = """update settings set value=? WHERE key='lastvacuumtime';"""
                parameters = (int(time.time()),)
                self.cur.execute(item, parameters)

        while True:
            item = shared.sqlSubmitQueue.get()
            if item == "commit":
                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == "database or disk is full":
                        logger.fatal(
                            "(While committing) Alert: Your disk or data storage volume is full. sqlThread will now exit."
                        )
                        shared.UISignalQueue.put(
                            (
                                "alert",
                                (
                                    tr.translateText("MainWindow", "Disk full"),
                                    tr.translateText(
                                        "MainWindow",
                                        "Alert: Your disk or data storage volume is full. Bitmessage will now exit.",
                                    ),
                                    True,
                                ),
                            )
                        )
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
            elif item == "exit":
                self.conn.close()
                logger.info("sqlThread exiting gracefully.")

                return
            elif item == "movemessagstoprog":
                logger.debug("the sqlThread is moving the messages.dat file to the local program directory.")

                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == "database or disk is full":
                        logger.fatal(
                            "(while movemessagstoprog) Alert: Your disk or data storage volume is full. sqlThread will now exit."
                        )
                        shared.UISignalQueue.put(
                            (
                                "alert",
                                (
                                    tr.translateText("MainWindow", "Disk full"),
                                    tr.translateText(
                                        "MainWindow",
                                        "Alert: Your disk or data storage volume is full. Bitmessage will now exit.",
                                    ),
                                    True,
                                ),
                            )
                        )
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                self.conn.close()
                shutil.move(shared.lookupAppdataFolder() + "messages.dat", "messages.dat")
                self.conn = sqlite3.connect("messages.dat")
                self.conn.text_factory = str
                self.cur = self.conn.cursor()
            elif item == "movemessagstoappdata":
                logger.debug("the sqlThread is moving the messages.dat file to the Appdata folder.")

                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == "database or disk is full":
                        logger.fatal(
                            "(while movemessagstoappdata) Alert: Your disk or data storage volume is full. sqlThread will now exit."
                        )
                        shared.UISignalQueue.put(
                            (
                                "alert",
                                (
                                    tr.translateText("MainWindow", "Disk full"),
                                    tr.translateText(
                                        "MainWindow",
                                        "Alert: Your disk or data storage volume is full. Bitmessage will now exit.",
                                    ),
                                    True,
                                ),
                            )
                        )
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                self.conn.close()
                shutil.move("messages.dat", shared.lookupAppdataFolder() + "messages.dat")
                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()
                try:
                    self.cur.execute(""" VACUUM """)
                except Exception as err:
                    if str(err) == "database or disk is full":
                        logger.fatal(
                            "(while deleteandvacuume) Alert: Your disk or data storage volume is full. sqlThread will now exit."
                        )
                        shared.UISignalQueue.put(
                            (
                                "alert",
                                (
                                    tr.translateText("MainWindow", "Disk full"),
                                    tr.translateText(
                                        "MainWindow",
                                        "Alert: Your disk or data storage volume is full. Bitmessage will now exit.",
                                    ),
                                    True,
                                ),
                            )
                        )
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
            else:
                parameters = shared.sqlSubmitQueue.get()
                # print 'item', item
                # print 'parameters', parameters
                try:
                    self.cur.execute(item, parameters)
                except Exception as err:
                    if str(err) == "database or disk is full":
                        logger.fatal(
                            "(while cur.execute) Alert: Your disk or data storage volume is full. sqlThread will now exit."
                        )
                        shared.UISignalQueue.put(
                            (
                                "alert",
                                (
                                    tr.translateText("MainWindow", "Disk full"),
                                    tr.translateText(
                                        "MainWindow",
                                        "Alert: Your disk or data storage volume is full. Bitmessage will now exit.",
                                    ),
                                    True,
                                ),
                            )
                        )
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                    else:
                        logger.fatal(
                            'Major error occurred when trying to execute a SQL statement within the sqlThread. Please tell Atheros about this error message or post it in the forum! Error occurred while trying to execute statement: "%s"  Here are the parameters; you might want to censor this data with asterisks (***) as it can contain private information: %s. Here is the actual error message thrown by the sqlThread: %s',
                            str(item),
                            str(repr(parameters)),
                            str(err),
                        )
                        logger.fatal("This program shall now abruptly exit!")

                    os._exit(0)

                shared.sqlReturnQueue.put(self.cur.fetchall())
Exemple #16
0
    def sendMsg(self):
        # Check to see if there are any messages queued to be sent
        queryreturn = sqlQuery(
            '''SELECT DISTINCT toaddress FROM sent WHERE (status='msgqueued' AND folder='sent')''')
        for row in queryreturn:  # For each address to which we need to send a message, check to see if we have its pubkey already.
            toaddress, = row
            toripe = decodeAddress(toaddress)[3]
            queryreturn = sqlQuery(
                '''SELECT hash FROM pubkeys WHERE hash=? ''', toripe)
            if queryreturn != []:  # If we have the needed pubkey, set the status to doingmsgpow (we'll do it further down)
                sqlExecute(
                    '''UPDATE sent SET status='doingmsgpow' WHERE toaddress=? AND status='msgqueued' ''',
                    toaddress)
            else:  # We don't have the needed pubkey. Set the status to 'awaitingpubkey' and request it if we haven't already
                if toripe in shared.neededPubkeys:
                    # We already sent a request for the pubkey
                    sqlExecute(
                        '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='msgqueued' ''', toaddress)
                    shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                        toripe, tr.translateText("MainWindow",'Encryption key was requested earlier.'))))
                else:
                    # We have not yet sent a request for the pubkey
                    sqlExecute(
                        '''UPDATE sent SET status='doingpubkeypow' WHERE toaddress=? AND status='msgqueued' ''',
                        toaddress)
                    shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                        toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.'))))
                    self.requestPubKey(toaddress)
        # 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.
        queryreturn = sqlQuery(
            '''SELECT toaddress, toripe, fromaddress, subject, message, ackdata, status FROM sent WHERE (status='doingmsgpow' or status='forcepow' or (status='toodifficult' and lastactiontime>?)) and folder='sent' ''',
            int(time.time()) - 2419200)
        for row in queryreturn:  # For each message we need to send..
            toaddress, toripe, fromaddress, subject, message, ackdata, status = row
            # 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.
            queryreturn = sqlQuery(
                '''SELECT hash FROM pubkeys WHERE hash=? ''',
                toripe)
            if queryreturn == [] and toripe not in shared.neededPubkeys:
                # We no longer have the needed pubkey and we haven't requested
                # it.
                with shared.printLock:
                    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'))

                sqlExecute(
                    '''UPDATE sent SET status='msgqueued' WHERE toaddress=? AND status='doingmsgpow' ''', toaddress)
                shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                    toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.'))))
                self.requestPubKey(toaddress)
                continue
            shared.ackdataForWhichImWatching[ackdata] = 0
            toStatus, toAddressVersionNumber, toStreamNumber, toHash = decodeAddress(
                toaddress)
            fromStatus, fromAddressVersionNumber, fromStreamNumber, fromHash = decodeAddress(
                fromaddress)
            shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key"))))
            with shared.printLock:
                print 'Found a message in our database that needs to be sent with this pubkey.'
                print 'First 150 characters of message:', repr(message[:150])


            # mark the pubkey as 'usedpersonally' so that we don't ever delete
            # it.
            sqlExecute(
                '''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=?''',
                toripe)
            # 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.
            queryreturn = sqlQuery(
                'SELECT transmitdata FROM pubkeys WHERE hash=?',
                toripe)
            if queryreturn == []:
                with shared.printLock:
                    sys.stderr.write(
                        '(within sendMsg) The needed pubkey was not found. This should never happen. Aborting send.\n')

                return
            for row in queryreturn:
                pubkeyPayload, = row

            # 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.
            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]
            # Mobile users may ask us to include their address's RIPE hash on a message
            # unencrypted. Before we actually do it the sending human must check a box
            # in the settings menu to allow it.
            if shared.isBitSetWithinBitfield(behaviorBitfield,30): # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
                if not shared.safeConfigGetBoolean('bitmessagesettings','willinglysendtomobile'): # if we are Not willing to include the receiver's RIPE hash on the message..
                    logger.info('The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send.')
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings.  %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
                    # if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
                    continue
            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
            
            # Let us fetch the amount of work required by the recipient.
            if toAddressVersionNumber == 2:
                requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                    ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nThere is no required difficulty for version 2 addresses like this."))))
            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
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nReceiver\'s required difficulty: %1 and %2").arg(str(float(
                    requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(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.
                        sqlExecute(
                            '''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''',
                            ackdata)
                        shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do.").arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(
                            requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(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)
                payload += encodeVarint(fromStreamNumber)
                payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                pubSigningKey = highlevelcrypto.privToPub(
                    privSigningKeyHex).decode('hex')
                pubEncryptionKey = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')

                payload += pubSigningKey[
                    1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
                payload += pubEncryptionKey[1:]

                payload += toHash  # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
                payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
                messageToTransmit = 'Subject:' + \
                    subject + '\n' + 'Body:' + message
                payload += encodeVarint(len(messageToTransmit))
                payload += messageToTransmit
                fullAckPayload = self.generateFullAckMessage(
                    ackdata, toStreamNumber, embeddedTime)  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
                payload += encodeVarint(len(fullAckPayload))
                payload += fullAckPayload
                signature = highlevelcrypto.sign(payload, privSigningKeyHex)
                payload += encodeVarint(len(signature))
                payload += signature

            if fromAddressVersionNumber == 3:
                payload = '\x01'  # Message version.
                payload += encodeVarint(fromAddressVersionNumber)
                payload += encodeVarint(fromStreamNumber)
                payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                pubSigningKey = highlevelcrypto.privToPub(
                    privSigningKeyHex).decode('hex')
                pubEncryptionKey = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')

                payload += pubSigningKey[
                    1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
                payload += pubEncryptionKey[1:]
                # If the receiver of our message is in our address book,
                # subscriptions list, or whitelist then we will allow them to
                # do the network-minimum proof of work. Let us check to see if
                # the receiver is in any of those lists.
                if shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(toaddress):
                    payload += encodeVarint(
                        shared.networkDefaultProofOfWorkNonceTrialsPerByte)
                    payload += encodeVarint(
                        shared.networkDefaultPayloadLengthExtraBytes)
                else:
                    payload += encodeVarint(shared.config.getint(
                        fromaddress, 'noncetrialsperbyte'))
                    payload += encodeVarint(shared.config.getint(
                        fromaddress, 'payloadlengthextrabytes'))

                payload += toHash  # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
                payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
                messageToTransmit = 'Subject:' + \
                    subject + '\n' + 'Body:' + message
                payload += encodeVarint(len(messageToTransmit))
                payload += messageToTransmit
                if shared.safeConfigGetBoolean(toaddress, 'chan'):
                    with shared.printLock:
                        print 'Not bothering to generate ackdata because we are sending to a chan.'
                    fullAckPayload = ''
                elif not shared.isBitSetWithinBitfield(behaviorBitfield,31):
                    with shared.printLock:
                        print 'Not bothering to generate ackdata because the receiver said that they won\'t relay it anyway.'
                    fullAckPayload = ''                    
                else:
                    fullAckPayload = self.generateFullAckMessage(
                        ackdata, toStreamNumber, embeddedTime)  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
                payload += encodeVarint(len(fullAckPayload))
                payload += fullAckPayload
                signature = highlevelcrypto.sign(payload, privSigningKeyHex)
                payload += encodeVarint(len(signature))
                payload += signature


            # We have assembled the data that will be encrypted.
            try:
                encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex'))
            except:
                sqlExecute('''UPDATE sent SET status='badkey' WHERE ackdata=?''', ackdata)
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
                continue
            encryptedPayload = embeddedTime + encodeVarint(toStreamNumber) + encrypted
            target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte)
            with shared.printLock:
                print '(For msg message) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes

            powStartTime = time.time()
            initialHash = hashlib.sha512(encryptedPayload).digest()
            trialValue, nonce = proofofwork.run(target, initialHash)
            with shared.printLock:
                print '(For msg message) Found proof of work', trialValue, 'Nonce:', nonce
                try:
                    print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.'
                except:
                    pass

            encryptedPayload = pack('>Q', nonce) + encryptedPayload

            inventoryHash = calculateInventoryHash(encryptedPayload)
            objectType = 'msg'
            shared.inventory[inventoryHash] = (
                objectType, toStreamNumber, encryptedPayload, int(time.time()))
            if shared.safeConfigGetBoolean(toaddress, 'chan'):
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Sent on %1").arg(unicode(
                    strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
            else:
                # not sending to a chan
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting on acknowledgement. Sent on %1").arg(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 or 'msgsentnoackexpected' status.
            if shared.safeConfigGetBoolean(toaddress, 'chan'):
                newStatus = 'msgsentnoackexpected'
            else:
                newStatus = 'msgsent'
            sqlExecute('''UPDATE sent SET msgid=?, status=? WHERE ackdata=?''',
                       inventoryHash,newStatus,ackdata)
Exemple #17
0
    def sendBroadcast(self):
        queryreturn = sqlQuery(
            '''SELECT fromaddress, subject, message, ackdata FROM sent WHERE status=? and folder='sent' ''', 'broadcastqueued')
        for row in queryreturn:
            fromaddress, subject, body, ackdata = row
            status, addressVersionNumber, streamNumber, ripe = decodeAddress(
                fromaddress)
            if addressVersionNumber <= 1:
                with shared.printLock:
                    sys.stderr.write(
                        'Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n')
                return
            # We need to convert our private keys to public keys in order
            # to include them.
            try:
                privSigningKeyBase58 = shared.config.get(
                    fromaddress, 'privsigningkey')
                privEncryptionKeyBase58 = shared.config.get(
                    fromaddress, 'privencryptionkey')
            except:
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                    ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                continue

            privSigningKeyHex = shared.decodeWalletImportFormat(
                privSigningKeyBase58).encode('hex')
            privEncryptionKeyHex = shared.decodeWalletImportFormat(
                privEncryptionKeyBase58).encode('hex')

            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('>Q', (int(time.time()) + random.randrange(
                -300, 300)))  # the current time plus or minus five minutes
            payload += encodeVarint(2)  # broadcast version
            payload += encodeVarint(streamNumber)

            dataToEncrypt = encodeVarint(2)  # broadcast version
            dataToEncrypt += encodeVarint(addressVersionNumber)
            dataToEncrypt += encodeVarint(streamNumber)
            dataToEncrypt += '\x00\x00\x00\x01'  # behavior bitfield
            dataToEncrypt += pubSigningKey[1:]
            dataToEncrypt += pubEncryptionKey[1:]
            if addressVersionNumber >= 3:
                dataToEncrypt += encodeVarint(shared.config.getint(fromaddress,'noncetrialsperbyte'))
                dataToEncrypt += encodeVarint(shared.config.getint(fromaddress,'payloadlengthextrabytes'))
            dataToEncrypt += '\x02' # message encoding type
            dataToEncrypt += encodeVarint(len('Subject:' + subject + '\n' + 'Body:' + body))  #Type 2 is simple UTF-8 message encoding per the documentation on the wiki.
            dataToEncrypt += 'Subject:' + subject + '\n' + 'Body:' + body
            signature = highlevelcrypto.sign(
                dataToEncrypt, privSigningKeyHex)
            dataToEncrypt += encodeVarint(len(signature))
            dataToEncrypt += signature

            # Encrypt the broadcast with the information contained in the broadcaster's address. Anyone who knows the address can generate 
            # the private encryption key to decrypt the broadcast. This provides virtually no privacy; its purpose is to keep questionable 
            # and illegal content from flowing through the Internet connections and being stored on the disk of 3rd parties. 
            privEncryptionKey = hashlib.sha512(encodeVarint(
                addressVersionNumber) + encodeVarint(streamNumber) + ripe).digest()[:32]
            pubEncryptionKey = pointMult(privEncryptionKey)
            payload += highlevelcrypto.encrypt(
                dataToEncrypt, pubEncryptionKey.encode('hex'))

            target = 2 ** 64 / ((len(
                payload) + shared.networkDefaultPayloadLengthExtraBytes + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)
            print '(For broadcast message) Doing proof of work...'
            shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast..."))))
            initialHash = hashlib.sha512(payload).digest()
            trialValue, nonce = proofofwork.run(target, initialHash)
            print '(For broadcast message) Found proof of work', trialValue, 'Nonce:', nonce

            payload = pack('>Q', nonce) + payload

            inventoryHash = calculateInventoryHash(payload)
            objectType = 'broadcast'
            shared.inventory[inventoryHash] = (
                objectType, streamNumber, payload, int(time.time()))
            with shared.printLock:
                print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex')
            shared.broadcastToSendDataQueues((
                streamNumber, 'sendinv', inventoryHash))

            shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Broadcast sent on %1").arg(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
            sqlExecute(
                'UPDATE sent SET msgid=?, status=?, lastactiontime=? WHERE ackdata=?',
                inventoryHash,
                'broadcastsent',
                int(time.time()),
                ackdata)
    def sendBroadcast(self):
        shared.sqlLock.acquire()
        t = ('broadcastqueued',)
        shared.sqlSubmitQueue.put(
            '''SELECT fromaddress, subject, message, ackdata FROM sent WHERE status=? and folder='sent' ''')
        shared.sqlSubmitQueue.put(t)
        queryreturn = shared.sqlReturnQueue.get()
        shared.sqlLock.release()
        for row in queryreturn:
            fromaddress, subject, body, ackdata = row
            status, addressVersionNumber, streamNumber, ripe = decodeAddress(
                fromaddress)
            """if addressVersionNumber == 2 and int(time.time()) < shared.encryptedBroadcastSwitchoverTime:
                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                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('>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)
                payload += '\x00\x00\x00\x01'  # behavior bitfield
                payload += pubSigningKey[1:]
                payload += pubEncryptionKey[1:]
                payload += ripe
                payload += '\x02'  # message encoding type
                payload += encodeVarint(len(
                    'Subject:' + subject + '\n' + 'Body:' + body))  # Type 2 is simple UTF-8 message encoding.
                payload += 'Subject:' + subject + '\n' + 'Body:' + body

                signature = highlevelcrypto.sign(payload, privSigningKeyHex)
                payload += encodeVarint(len(signature))
                payload += signature

                target = 2 ** 64 / ((len(
                    payload) + shared.networkDefaultPayloadLengthExtraBytes + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)
                print '(For broadcast message) Doing proof of work...'
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                    ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast..."))))
                initialHash = hashlib.sha512(payload).digest()
                trialValue, nonce = proofofwork.run(target, initialHash)
                print '(For broadcast message) Found proof of work', trialValue, 'Nonce:', nonce

                payload = pack('>Q', nonce) + payload

                inventoryHash = calculateInventoryHash(payload)
                objectType = 'broadcast'
                shared.inventory[inventoryHash] = (
                    objectType, streamNumber, payload, int(time.time()))
                print 'Broadcasting inv for my broadcast (within sendBroadcast function):', inventoryHash.encode('hex')
                shared.broadcastToSendDataQueues((
                    streamNumber, 'sendinv', inventoryHash))

                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Broadcast sent on %1").arg(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
                shared.sqlLock.acquire()
                t = ('broadcastsent', int(
                    time.time()), fromaddress, subject, body, 'broadcastqueued')
                shared.sqlSubmitQueue.put(
                    'UPDATE sent SET status=?, lastactiontime=? WHERE fromaddress=? AND subject=? AND message=? AND status=?')
                shared.sqlSubmitQueue.put(t)
                queryreturn = shared.sqlReturnQueue.get()
                shared.sqlSubmitQueue.put('commit')
                shared.sqlLock.release()"""
            if addressVersionNumber == 2 or addressVersionNumber == 3:
                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                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('>Q', (int(time.time()) + random.randrange(
                    -300, 300)))  # the current time plus or minus five minutes
                payload += encodeVarint(2)  # broadcast version
                payload += encodeVarint(streamNumber)

                dataToEncrypt = encodeVarint(2)  # broadcast version
                dataToEncrypt += encodeVarint(addressVersionNumber)
                dataToEncrypt += encodeVarint(streamNumber)
                dataToEncrypt += '\x00\x00\x00\x01'  # behavior bitfield
                dataToEncrypt += pubSigningKey[1:]
                dataToEncrypt += pubEncryptionKey[1:]
		if addressVersionNumber >= 3:
                    dataToEncrypt += encodeVarint(shared.config.getint(fromaddress,'noncetrialsperbyte'))
                    dataToEncrypt += encodeVarint(shared.config.getint(fromaddress,'payloadlengthextrabytes'))
                dataToEncrypt += '\x02' # message encoding type
                dataToEncrypt += encodeVarint(len('Subject:' + subject + '\n' + 'Body:' + body))  #Type 2 is simple UTF-8 message encoding per the documentation on the wiki.
                dataToEncrypt += 'Subject:' + subject + '\n' + 'Body:' + body
                signature = highlevelcrypto.sign(
                    dataToEncrypt, privSigningKeyHex)
                dataToEncrypt += encodeVarint(len(signature))
                dataToEncrypt += signature

                # Encrypt the broadcast with the information contained in the broadcaster's address. Anyone who knows the address can generate 
                # the private encryption key to decrypt the broadcast. This provides virtually no privacy; its purpose is to keep questionable 
                # and illegal content from flowing through the Internet connections and being stored on the disk of 3rd parties. 
                privEncryptionKey = hashlib.sha512(encodeVarint(
                    addressVersionNumber) + encodeVarint(streamNumber) + ripe).digest()[:32]
                pubEncryptionKey = pointMult(privEncryptionKey)
                payload += highlevelcrypto.encrypt(
                    dataToEncrypt, pubEncryptionKey.encode('hex'))

                target = 2 ** 64 / ((len(
                    payload) + shared.networkDefaultPayloadLengthExtraBytes + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)
                print '(For broadcast message) Doing proof of work...'
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                    ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast..."))))
                initialHash = hashlib.sha512(payload).digest()
                trialValue, nonce = proofofwork.run(target, initialHash)
                print '(For broadcast message) Found proof of work', trialValue, 'Nonce:', nonce

                payload = pack('>Q', nonce) + payload

                inventoryHash = calculateInventoryHash(payload)
                objectType = 'broadcast'
                shared.inventory[inventoryHash] = (
                    objectType, streamNumber, payload, int(time.time()))
                print 'sending inv (within sendBroadcast function)'
                shared.broadcastToSendDataQueues((
                    streamNumber, 'sendinv', inventoryHash))

                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Broadcast sent on %1").arg(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
                shared.sqlLock.acquire()
                t = ('broadcastsent', int(
                    time.time()), fromaddress, subject, body, 'broadcastqueued')
                shared.sqlSubmitQueue.put(
                    'UPDATE sent SET status=?, lastactiontime=? WHERE fromaddress=? AND subject=? AND message=? AND status=?')
                shared.sqlSubmitQueue.put(t)
                queryreturn = shared.sqlReturnQueue.get()
                shared.sqlSubmitQueue.put('commit')
                shared.sqlLock.release()
            else:
                with shared.printLock:
                    sys.stderr.write(
                        'Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n')
    def sendMsg(self):
        # Check to see if there are any messages queued to be sent
        shared.sqlLock.acquire()
        shared.sqlSubmitQueue.put(
            '''SELECT DISTINCT toaddress FROM sent WHERE (status='msgqueued' AND folder='sent')''')
        shared.sqlSubmitQueue.put('')
        queryreturn = shared.sqlReturnQueue.get()
        shared.sqlLock.release()
        for row in queryreturn:  # For each address to which we need to send a message, check to see if we have its pubkey already.
            toaddress, = row
            toripe = decodeAddress(toaddress)[3]
            shared.sqlLock.acquire()
            shared.sqlSubmitQueue.put(
                '''SELECT hash FROM pubkeys WHERE hash=? ''')
            shared.sqlSubmitQueue.put((toripe,))
            queryreturn = shared.sqlReturnQueue.get()
            shared.sqlLock.release()
            if queryreturn != []:  # If we have the needed pubkey, set the status to doingmsgpow (we'll do it further down)
                t = (toaddress,)
                shared.sqlLock.acquire()
                shared.sqlSubmitQueue.put(
                    '''UPDATE sent SET status='doingmsgpow' WHERE toaddress=? AND status='msgqueued' ''')
                shared.sqlSubmitQueue.put(t)
                shared.sqlReturnQueue.get()
                shared.sqlSubmitQueue.put('commit')
                shared.sqlLock.release()
            else:  # We don't have the needed pubkey. Set the status to 'awaitingpubkey' and request it if we haven't already
                if toripe in shared.neededPubkeys:
                    # We already sent a request for the pubkey
                    t = (toaddress,)
                    shared.sqlLock.acquire()
                    shared.sqlSubmitQueue.put(
                        '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='msgqueued' ''')
                    shared.sqlSubmitQueue.put(t)
                    shared.sqlReturnQueue.get()
                    shared.sqlSubmitQueue.put('commit')
                    shared.sqlLock.release()
                    shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                        toripe, tr.translateText("MainWindow",'Encryption key was requested earlier.'))))
                else:
                    # We have not yet sent a request for the pubkey
                    t = (toaddress,)
                    shared.sqlLock.acquire()
                    shared.sqlSubmitQueue.put(
                        '''UPDATE sent SET status='doingpubkeypow' WHERE toaddress=? AND status='msgqueued' ''')
                    shared.sqlSubmitQueue.put(t)
                    shared.sqlReturnQueue.get()
                    shared.sqlSubmitQueue.put('commit')
                    shared.sqlLock.release()
                    shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                        toripe, tr.translateText("MainWindow",'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 each message we need to send..
            toaddress, toripe, fromaddress, subject, message, ackdata, status = row
            # 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,))
            queryreturn = shared.sqlReturnQueue.get()
            shared.sqlLock.release()
            if queryreturn == [] and toripe not in shared.neededPubkeys:
                # We no longer have the needed pubkey and we haven't requested
                # it.
                with shared.printLock:
                    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'))

                t = (toaddress,)
                shared.sqlLock.acquire()
                shared.sqlSubmitQueue.put(
                    '''UPDATE sent SET status='msgqueued' WHERE toaddress=? AND status='doingmsgpow' ''')
                shared.sqlSubmitQueue.put(t)
                shared.sqlReturnQueue.get()
                shared.sqlSubmitQueue.put('commit')
                shared.sqlLock.release()
                shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                    toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.'))))
                self.requestPubKey(toaddress)
                continue
            shared.ackdataForWhichImWatching[ackdata] = 0
            toStatus, toAddressVersionNumber, toStreamNumber, toHash = decodeAddress(
                toaddress)
            fromStatus, fromAddressVersionNumber, fromStreamNumber, fromHash = decodeAddress(
                fromaddress)
            shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key"))))
            with shared.printLock:
                print 'Found a message in our database that needs to be sent with this pubkey.'
                print 'First 150 characters of message:', repr(message[:150])


            # mark the pubkey as 'usedpersonally' so that we don't ever delete
            # it.
            shared.sqlLock.acquire()
            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 == []:
                with shared.printLock:
                    sys.stderr.write(
                        '(within sendMsg) The needed pubkey was not found. This should never happen. Aborting send.\n')

                return
            for row in queryreturn:
                pubkeyPayload, = row

            # 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.
            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
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                    ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nThere is no required difficulty for version 2 addresses like this."))))
            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
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nReceiver\'s required difficulty: %1 and %2").arg(str(float(
                    requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(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=? ''')
                        shared.sqlSubmitQueue.put(t)
                        shared.sqlReturnQueue.get()
                        shared.sqlSubmitQueue.put('commit')
                        shared.sqlLock.release()
                        shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do.").arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(
                            requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(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)
                payload += encodeVarint(fromStreamNumber)
                payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                pubSigningKey = highlevelcrypto.privToPub(
                    privSigningKeyHex).decode('hex')
                pubEncryptionKey = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')

                payload += pubSigningKey[
                    1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
                payload += pubEncryptionKey[1:]

                payload += toHash  # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
                payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
                messageToTransmit = 'Subject:' + \
                    subject + '\n' + 'Body:' + message
                payload += encodeVarint(len(messageToTransmit))
                payload += messageToTransmit
                fullAckPayload = self.generateFullAckMessage(
                    ackdata, toStreamNumber, embeddedTime)  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
                payload += encodeVarint(len(fullAckPayload))
                payload += fullAckPayload
                signature = highlevelcrypto.sign(payload, privSigningKeyHex)
                payload += encodeVarint(len(signature))
                payload += signature

            if fromAddressVersionNumber == 3:
                payload = '\x01'  # Message version.
                payload += encodeVarint(fromAddressVersionNumber)
                payload += encodeVarint(fromStreamNumber)
                payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                pubSigningKey = highlevelcrypto.privToPub(
                    privSigningKeyHex).decode('hex')
                pubEncryptionKey = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')

                payload += pubSigningKey[
                    1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
                payload += pubEncryptionKey[1:]
                # If the receiver of our message is in our address book,
                # subscriptions list, or whitelist then we will allow them to
                # do the network-minimum proof of work. Let us check to see if
                # the receiver is in any of those lists.
                if shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(toaddress):
                    payload += encodeVarint(
                        shared.networkDefaultProofOfWorkNonceTrialsPerByte)
                    payload += encodeVarint(
                        shared.networkDefaultPayloadLengthExtraBytes)
                else:
                    payload += encodeVarint(shared.config.getint(
                        fromaddress, 'noncetrialsperbyte'))
                    payload += encodeVarint(shared.config.getint(
                        fromaddress, 'payloadlengthextrabytes'))

                payload += toHash  # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
                payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
                messageToTransmit = 'Subject:' + \
                    subject + '\n' + 'Body:' + message
                payload += encodeVarint(len(messageToTransmit))
                payload += messageToTransmit
                fullAckPayload = self.generateFullAckMessage(
                    ackdata, toStreamNumber, embeddedTime)  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
                payload += encodeVarint(len(fullAckPayload))
                payload += fullAckPayload
                signature = highlevelcrypto.sign(payload, privSigningKeyHex)
                payload += encodeVarint(len(signature))
                payload += signature


            # We have assembled the data that will be encrypted.
            try:
                encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex'))
            except:
                shared.sqlLock.acquire()
                t = (ackdata,)
                shared.sqlSubmitQueue.put('''UPDATE sent SET status='badkey' WHERE ackdata=?''')
                shared.sqlSubmitQueue.put(t)
                queryreturn = shared.sqlReturnQueue.get()
                shared.sqlSubmitQueue.put('commit')
                shared.sqlLock.release()
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
                continue
            encryptedPayload = embeddedTime + encodeVarint(toStreamNumber) + encrypted
            target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte)
            with shared.printLock:
                print '(For msg message) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes

            powStartTime = time.time()
            initialHash = hashlib.sha512(encryptedPayload).digest()
            trialValue, nonce = proofofwork.run(target, initialHash)
            with shared.printLock:
                print '(For msg message) Found proof of work', trialValue, 'Nonce:', nonce
                try:
                    print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.'
                except:
                    pass

            encryptedPayload = pack('>Q', nonce) + encryptedPayload

            inventoryHash = calculateInventoryHash(encryptedPayload)
            objectType = 'msg'
            shared.inventory[inventoryHash] = (
                objectType, toStreamNumber, encryptedPayload, int(time.time()))
            shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting on acknowledgement. Sent on %1").arg(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 = (ackdata,)
            shared.sqlSubmitQueue.put('''UPDATE sent SET status='msgsent' WHERE ackdata=?''')
            shared.sqlSubmitQueue.put(t)
            queryreturn = shared.sqlReturnQueue.get()
            shared.sqlSubmitQueue.put('commit')
            shared.sqlLock.release()
    def run(self):
        timeWeLastClearedInventoryAndPubkeysTables = 0

        while True:
            shared.UISignalQueue.put(
                ("updateStatusBar", "Doing housekeeping (Flushing inventory in memory to disk...)")
            )

            with shared.inventoryLock:  # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock.
                with SqlBulkExecute() as sql:
                    for hash, storedValue in shared.inventory.items():
                        objectType, streamNumber, payload, receivedTime, tag = storedValue
                        if int(time.time()) - 3600 > receivedTime:
                            sql.execute(
                                """INSERT INTO inventory VALUES (?,?,?,?,?,?)""",
                                hash,
                                objectType,
                                streamNumber,
                                payload,
                                receivedTime,
                                tag,
                            )
                            del shared.inventory[hash]
            shared.UISignalQueue.put(("updateStatusBar", ""))
            shared.broadcastToSendDataQueues(
                (0, "pong", "no data")
            )  # commands the sendData threads to send out a pong message if they haven't sent anything else in the last five minutes. The socket timeout-time is 10 minutes.
            # If we are running as a daemon then we are going to fill up the UI
            # queue which will never be handled by a UI. We should clear it to
            # save memory.
            if shared.safeConfigGetBoolean("bitmessagesettings", "daemon"):
                shared.UISignalQueue.queue.clear()
            if timeWeLastClearedInventoryAndPubkeysTables < int(time.time()) - 7380:
                timeWeLastClearedInventoryAndPubkeysTables = int(time.time())
                # inventory (moves data from the inventory data structure to
                # the on-disk sql database)
                # inventory (clears pubkeys after 28 days and everything else
                # after 2 days and 12 hours)
                sqlExecute(
                    """DELETE FROM inventory WHERE (receivedtime<? AND objecttype<>'pubkey') OR (receivedtime<?  AND objecttype='pubkey') """,
                    int(time.time()) - shared.lengthOfTimeToLeaveObjectsInInventory,
                    int(time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys,
                )

                # pubkeys
                sqlExecute(
                    """DELETE FROM pubkeys WHERE time<? AND usedpersonally='no' """,
                    int(time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys,
                )

                queryreturn = sqlQuery(
                    """select toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, status, pubkeyretrynumber, msgretrynumber FROM sent WHERE ((status='awaitingpubkey' OR status='msgsent') AND folder='sent') """
                )  # If the message's folder='trash' then we'll ignore it.
                for row in queryreturn:
                    if len(row) < 5:
                        with shared.printLock:
                            sys.stderr.write(
                                "Something went wrong in the singleCleaner thread: a query did not return the requested fields. "
                                + repr(row)
                            )
                        time.sleep(3)

                        break
                    toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, status, pubkeyretrynumber, msgretrynumber = (
                        row
                    )
                    if status == "awaitingpubkey":
                        if int(time.time()) - lastactiontime > (
                            shared.maximumAgeOfAnObjectThatIAmWillingToAccept * (2 ** (pubkeyretrynumber))
                        ):
                            print "It has been a long time and we haven't heard a response to our getpubkey request. Sending again."
                            try:
                                del shared.neededPubkeys[
                                    toripe
                                ]  # We need to take this entry out of the shared.neededPubkeys structure because the shared.workerQueue checks to see whether the entry is already present and will not do the POW and send the message because it assumes that it has already done it recently.
                            except:
                                pass

                            shared.UISignalQueue.put(
                                ("updateStatusBar", "Doing work necessary to again attempt to request a public key...")
                            )
                            t = ()
                            sqlExecute(
                                """UPDATE sent SET lastactiontime=?, pubkeyretrynumber=?, status='msgqueued' WHERE toripe=?""",
                                int(time.time()),
                                pubkeyretrynumber + 1,
                                toripe,
                            )
                            shared.workerQueue.put(("sendmessage", ""))
                    else:  # status == msgsent
                        if int(time.time()) - lastactiontime > (
                            shared.maximumAgeOfAnObjectThatIAmWillingToAccept * (2 ** (msgretrynumber))
                        ):
                            print "It has been a long time and we haven't heard an acknowledgement to our msg. Sending again."
                            sqlExecute(
                                """UPDATE sent SET lastactiontime=?, msgretrynumber=?, status=? WHERE ackdata=?""",
                                int(time.time()),
                                msgretrynumber + 1,
                                "msgqueued",
                                ackdata,
                            )
                            shared.workerQueue.put(("sendmessage", ""))
                            shared.UISignalQueue.put(
                                ("updateStatusBar", "Doing work necessary to again attempt to deliver a message...")
                            )

                # Let's also clear and reload shared.inventorySets to keep it from
                # taking up an unnecessary amount of memory.
                for streamNumber in shared.inventorySets:
                    shared.inventorySets[streamNumber] = set()
                    queryData = sqlQuery("""SELECT hash FROM inventory WHERE streamnumber=?""", streamNumber)
                    for row in queryData:
                        shared.inventorySets[streamNumber].add(row[0])
                with shared.inventoryLock:
                    for hash, storedValue in shared.inventory.items():
                        objectType, streamNumber, payload, receivedTime, tag = storedValue
                        if streamNumber in shared.inventorySets:
                            shared.inventorySets[streamNumber].add(hash)

            # Let us write out the knowNodes to disk if there is anything new to write out.
            if shared.needToWriteKnownNodesToDisk:
                shared.knownNodesLock.acquire()
                output = open(shared.appdata + "knownnodes.dat", "wb")
                try:
                    pickle.dump(shared.knownNodes, output)
                    output.close()
                except Exception as err:
                    if "Errno 28" in str(err):
                        logger.fatal(
                            "(while receiveDataThread shared.needToWriteKnownNodesToDisk) Alert: Your disk or data storage volume is full. "
                        )
                        shared.UISignalQueue.put(
                            (
                                "alert",
                                (
                                    tr.translateText("MainWindow", "Disk full"),
                                    tr.translateText(
                                        "MainWindow",
                                        "Alert: Your disk or data storage volume is full. Bitmessage will now exit.",
                                    ),
                                    True,
                                ),
                            )
                        )
                        if shared.daemon:
                            os._exit(0)
                shared.knownNodesLock.release()
                shared.needToWriteKnownNodesToDisk = False
            time.sleep(300)
Exemple #21
0
    def run(self):
        self.conn = sqlite3.connect(shared.appdata + 'messages.dat')
        self.conn.text_factory = str
        self.cur = self.conn.cursor()

        try:
            self.cur.execute(
                '''CREATE TABLE inbox (msgid blob, toaddress text, fromaddress text, subject text, received text, message text, folder text, encodingtype int, read bool, UNIQUE(msgid) ON CONFLICT REPLACE)'''
            )
            self.cur.execute(
                '''CREATE TABLE sent (msgid blob, toaddress text, toripe blob, fromaddress text, subject text, message text, ackdata blob, lastactiontime integer, status text, pubkeyretrynumber integer, msgretrynumber integer, folder text, encodingtype int)'''
            )
            self.cur.execute(
                '''CREATE TABLE subscriptions (label text, address text, enabled bool)'''
            )
            self.cur.execute(
                '''CREATE TABLE addressbook (label text, address text)''')
            self.cur.execute(
                '''CREATE TABLE blacklist (label text, address text, enabled bool)'''
            )
            self.cur.execute(
                '''CREATE TABLE whitelist (label text, address text, enabled bool)'''
            )
            """
            Explanation of what is in the pubkeys table:
                The hash is the RIPEMD160 hash that is encoded in the Bitmessage address.
              
                transmitdata /was/ literally the data that was included in the Bitmessage pubkey message when it arrived, 
                except for the 24 byte protocol header- ie, it started with the POW nonce. Since protocol v3, to maintain
                backwards compability, the data format of the data on disk is staying the same even though the wire format has changed.
              
                time is the time that the pubkey was broadcast on the network same as with every other type of Bitmessage object.
              
                usedpersonally is set to "yes" if we have used the key personally. This keeps us from deleting it because we may want to
                reply to a message in the future. This field is not a bool because we may need more flexability in the future and it doesn't
                take up much more space anyway.
            """
            self.cur.execute(
                '''CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)'''
            )
            self.cur.execute(
                '''CREATE TABLE inventory (hash blob, objecttype int, streamnumber int, payload blob, expirestime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)'''
            )
            self.cur.execute(
                '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)'''
            )
            self.cur.execute(
                '''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)'''
            )
            self.cur.execute('''INSERT INTO settings VALUES('version','7')''')
            self.cur.execute(
                '''INSERT INTO settings VALUES('lastvacuumtime',?)''',
                (int(time.time()), ))
            self.cur.execute(
                '''CREATE TABLE objectprocessorqueue (objecttype int, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)'''
            )
            self.conn.commit()
            logger.info('Created messages database file')
        except Exception as err:
            if str(err) == 'table inbox already exists':
                logger.debug('Database file already exists.')

            else:
                sys.stderr.write(
                    'ERROR trying to create database file (message.dat). Error message: %s\n'
                    % str(err))
                os._exit(0)

        if shared.config.getint('bitmessagesettings', 'settingsversion') == 1:
            shared.config.set('bitmessagesettings', 'settingsversion', '2')
            # If the settings version is equal to 2 or 3 then the
            # sqlThread will modify the pubkeys table and change
            # the settings version to 4.
            shared.config.set('bitmessagesettings', 'socksproxytype', 'none')
            shared.config.set('bitmessagesettings', 'sockshostname',
                              'localhost')
            shared.config.set('bitmessagesettings', 'socksport', '9050')
            shared.config.set('bitmessagesettings', 'socksauthentication',
                              'false')
            shared.config.set('bitmessagesettings', 'socksusername', '')
            shared.config.set('bitmessagesettings', 'sockspassword', '')
            shared.config.set('bitmessagesettings', 'sockslisten', 'false')
            shared.config.set('bitmessagesettings', 'keysencrypted', 'false')
            shared.config.set('bitmessagesettings', 'messagesencrypted',
                              'false')
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        # People running earlier versions of PyBitmessage do not have the
        # usedpersonally field in their pubkeys table. Let's add it.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 2:
            item = '''ALTER TABLE pubkeys ADD usedpersonally text DEFAULT 'no' '''
            parameters = ''
            self.cur.execute(item, parameters)
            self.conn.commit()

            shared.config.set('bitmessagesettings', 'settingsversion', '3')
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        # People running earlier versions of PyBitmessage do not have the
        # encodingtype field in their inbox and sent tables or the read field
        # in the inbox table. Let's add them.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 3:
            item = '''ALTER TABLE inbox ADD encodingtype int DEFAULT '2' '''
            parameters = ''
            self.cur.execute(item, parameters)

            item = '''ALTER TABLE inbox ADD read bool DEFAULT '1' '''
            parameters = ''
            self.cur.execute(item, parameters)

            item = '''ALTER TABLE sent ADD encodingtype int DEFAULT '2' '''
            parameters = ''
            self.cur.execute(item, parameters)
            self.conn.commit()

            shared.config.set('bitmessagesettings', 'settingsversion', '4')

        if shared.config.getint('bitmessagesettings', 'settingsversion') == 4:
            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')
        # From now on, let us keep a 'version' embedded in the messages.dat
        # file so that when we make changes to the database, the database
        # version we are on can stay embedded in the messages.dat file. Let us
        # check to see if the settings table exists yet.
        item = '''SELECT name FROM sqlite_master WHERE type='table' AND name='settings';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if self.cur.fetchall() == []:
            # The settings table doesn't exist. We need to make it.
            logger.debug(
                'In messages.dat database, creating new \'settings\' table.')
            self.cur.execute(
                '''CREATE TABLE settings (key text, value blob, UNIQUE(key) ON CONFLICT REPLACE)'''
            )
            self.cur.execute('''INSERT INTO settings VALUES('version','1')''')
            self.cur.execute(
                '''INSERT INTO settings VALUES('lastvacuumtime',?)''',
                (int(time.time()), ))
            logger.debug(
                'In messages.dat database, removing an obsolete field from the pubkeys table.'
            )
            self.cur.execute(
                '''CREATE TEMPORARY TABLE pubkeys_backup(hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE);'''
            )
            self.cur.execute(
                '''INSERT INTO pubkeys_backup SELECT hash, transmitdata, time, usedpersonally FROM pubkeys;'''
            )
            self.cur.execute('''DROP TABLE pubkeys''')
            self.cur.execute(
                '''CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)'''
            )
            self.cur.execute(
                '''INSERT INTO pubkeys SELECT hash, transmitdata, time, usedpersonally FROM pubkeys_backup;'''
            )
            self.cur.execute('''DROP TABLE pubkeys_backup;''')
            logger.debug(
                'Deleting all pubkeys from inventory. They will be redownloaded and then saved with the correct times.'
            )
            self.cur.execute(
                '''delete from inventory where objecttype = 'pubkey';''')
            logger.debug(
                'replacing Bitmessage announcements mailing list with a new one.'
            )
            self.cur.execute(
                '''delete from subscriptions where address='BM-BbkPSZbzPwpVcYZpU4yHwf9ZPEapN5Zx' '''
            )
            self.cur.execute(
                '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)'''
            )
            logger.debug('Commiting.')
            self.conn.commit()
            logger.debug(
                'Vacuuming message.dat. You might notice that the file size gets much smaller.'
            )
            self.cur.execute(''' VACUUM ''')

        # After code refactoring, the possible status values for sent messages
        # have changed.
        self.cur.execute(
            '''update sent set status='doingmsgpow' where status='doingpow'  '''
        )
        self.cur.execute(
            '''update sent set status='msgsent' where status='sentmessage'  '''
        )
        self.cur.execute(
            '''update sent set status='doingpubkeypow' where status='findingpubkey'  '''
        )
        self.cur.execute(
            '''update sent set status='broadcastqueued' where status='broadcastpending'  '''
        )
        self.conn.commit()

        if not shared.config.has_option('bitmessagesettings', 'sockslisten'):
            shared.config.set('bitmessagesettings', 'sockslisten', 'false')

        ensureNamecoinOptions()
        """# Add a new column to the inventory table to store the first 20 bytes of encrypted messages to support Android app
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if int(self.cur.fetchall()[0][0]) == 1:
            print 'upgrading database'
            item = '''ALTER TABLE inventory ADD first20bytesofencryptedmessage blob DEFAULT '' '''
            parameters = ''
            self.cur.execute(item, parameters)
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (2,)
            self.cur.execute(item, parameters)"""

        # Let's get rid of the first20bytesofencryptedmessage field in the inventory table.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if int(self.cur.fetchall()[0][0]) == 2:
            logger.debug(
                'In messages.dat database, removing an obsolete field from the inventory table.'
            )
            self.cur.execute(
                '''CREATE TEMPORARY TABLE inventory_backup(hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE);'''
            )
            self.cur.execute(
                '''INSERT INTO inventory_backup SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory;'''
            )
            self.cur.execute('''DROP TABLE inventory''')
            self.cur.execute(
                '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE)'''
            )
            self.cur.execute(
                '''INSERT INTO inventory SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory_backup;'''
            )
            self.cur.execute('''DROP TABLE inventory_backup;''')
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (3, )
            self.cur.execute(item, parameters)

        # Add a new column to the inventory table to store tags.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 1 or currentVersion == 3:
            logger.debug(
                'In messages.dat database, adding tag field to the inventory table.'
            )
            item = '''ALTER TABLE inventory ADD tag blob DEFAULT '' '''
            parameters = ''
            self.cur.execute(item, parameters)
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (4, )
            self.cur.execute(item, parameters)

        if not shared.config.has_option('bitmessagesettings', 'userlocale'):
            shared.config.set('bitmessagesettings', 'userlocale', 'system')
        if not shared.config.has_option('bitmessagesettings',
                                        'sendoutgoingconnections'):
            shared.config.set('bitmessagesettings', 'sendoutgoingconnections',
                              'True')

        # Raise the default required difficulty from 1 to 2
        # With the change to protocol v3, this is obsolete.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 6:
            """if int(shared.config.get('bitmessagesettings','defaultnoncetrialsperbyte')) == shared.networkDefaultProofOfWorkNonceTrialsPerByte:
                shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte', str(shared.networkDefaultProofOfWorkNonceTrialsPerByte * 2))
                """
            shared.config.set('bitmessagesettings', 'settingsversion', '7')

        # Add a new column to the pubkeys table to store the address version.
        # We're going to trash all of our pubkeys and let them be redownloaded.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 4:
            self.cur.execute('''DROP TABLE pubkeys''')
            self.cur.execute(
                '''CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)'''
            )
            self.cur.execute(
                '''delete from inventory where objecttype = 'pubkey';''')
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (5, )
            self.cur.execute(item, parameters)

        if not shared.config.has_option('bitmessagesettings', 'useidenticons'):
            shared.config.set('bitmessagesettings', 'useidenticons', 'True')
        if not shared.config.has_option('bitmessagesettings',
                                        'identiconsuffix'):  # acts as a salt
            shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(
                random.choice(
                    "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
                ) for x in range(12)
            ))  # a twelve character pseudo-password to salt the identicons

        #Add settings to support no longer resending messages after a certain period of time even if we never get an ack
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 7:
            shared.config.set('bitmessagesettings', 'stopresendingafterxdays',
                              '')
            shared.config.set('bitmessagesettings',
                              'stopresendingafterxmonths', '')
            #shared.config.set(
            shared.config.set('bitmessagesettings', 'settingsversion', '8')
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        # Add a new table: objectprocessorqueue with which to hold objects
        # that have yet to be processed if the user shuts down Bitmessage.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 5:
            self.cur.execute('''DROP TABLE knownnodes''')
            self.cur.execute(
                '''CREATE TABLE objectprocessorqueue (objecttype text, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)'''
            )
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (6, )
            self.cur.execute(item, parameters)

        # changes related to protocol v3
#    In table inventory and objectprocessorqueue, objecttype is now an integer (it was a human-friendly string previously)
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 6:
            logger.debug(
                'In messages.dat database, dropping and recreating the inventory table.'
            )
            self.cur.execute('''DROP TABLE inventory''')
            self.cur.execute(
                '''CREATE TABLE inventory (hash blob, objecttype int, streamnumber int, payload blob, expirestime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)'''
            )
            self.cur.execute('''DROP TABLE objectprocessorqueue''')
            self.cur.execute(
                '''CREATE TABLE objectprocessorqueue (objecttype int, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)'''
            )
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (7, )
            self.cur.execute(item, parameters)
            logger.debug(
                'Finished dropping and recreating the inventory table.')

        # With the change to protocol version 3, reset the user-settable difficulties to 1
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 8:
            shared.config.set(
                'bitmessagesettings', 'defaultnoncetrialsperbyte',
                str(shared.networkDefaultProofOfWorkNonceTrialsPerByte))
            shared.config.set(
                'bitmessagesettings', 'defaultpayloadlengthextrabytes',
                str(shared.networkDefaultPayloadLengthExtraBytes))
            previousTotalDifficulty = int(
                shared.config.getint('bitmessagesettings',
                                     'maxacceptablenoncetrialsperbyte')) / 320
            previousSmallMessageDifficulty = int(
                shared.config.getint(
                    'bitmessagesettings',
                    'maxacceptablepayloadlengthextrabytes')) / 14000
            shared.config.set('bitmessagesettings',
                              'maxacceptablenoncetrialsperbyte',
                              str(previousTotalDifficulty * 1000))
            shared.config.set('bitmessagesettings',
                              'maxacceptablepayloadlengthextrabytes',
                              str(previousSmallMessageDifficulty * 1000))
            shared.config.set('bitmessagesettings', 'settingsversion', '9')

        # Adjust the required POW values for each of this user's addresses to conform to protocol v3 norms.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 9:
            for addressInKeysFile in shared.config.sections():
                try:
                    previousTotalDifficulty = float(
                        shared.config.getint(addressInKeysFile,
                                             'noncetrialsperbyte')) / 320
                    previousSmallMessageDifficulty = float(
                        shared.config.getint(
                            addressInKeysFile,
                            'payloadlengthextrabytes')) / 14000
                    if previousTotalDifficulty <= 2:
                        previousTotalDifficulty = 1
                    if previousSmallMessageDifficulty < 1:
                        previousSmallMessageDifficulty = 1
                    shared.config.set(addressInKeysFile, 'noncetrialsperbyte',
                                      str(int(previousTotalDifficulty * 1000)))
                    shared.config.set(
                        addressInKeysFile, 'payloadlengthextrabytes',
                        str(int(previousSmallMessageDifficulty * 1000)))
                except:
                    continue
            shared.config.set('bitmessagesettings', 'maxdownloadrate', '0')
            shared.config.set('bitmessagesettings', 'maxuploadrate', '0')
            shared.config.set('bitmessagesettings', 'settingsversion', '10')
            with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                shared.config.write(configfile)

        # Are you hoping to add a new option to the keys.dat file of existing
        # Bitmessage users or modify the SQLite database? Add it right above this line!

        try:
            testpayload = '\x00\x00'
            t = ('1234', 1, testpayload, '12345678', 'no')
            self.cur.execute('''INSERT INTO pubkeys VALUES(?,?,?,?,?)''', t)
            self.conn.commit()
            self.cur.execute(
                '''SELECT transmitdata FROM pubkeys WHERE hash='1234' ''')
            queryreturn = self.cur.fetchall()
            for row in queryreturn:
                transmitdata, = row
            self.cur.execute('''DELETE FROM pubkeys WHERE hash='1234' ''')
            self.conn.commit()
            if transmitdata == '':
                logger.fatal(
                    'Problem: The version of SQLite you have cannot store Null values. Please download and install the latest revision of your version of Python (for example, the latest Python 2.7 revision) and try again.\n'
                )
                logger.fatal(
                    'PyBitmessage will now exit very abruptly. You may now see threading errors related to this abrupt exit but the problem you need to solve is related to SQLite.\n\n'
                )
                os._exit(0)
        except Exception as err:
            if str(err) == 'database or disk is full':
                logger.fatal(
                    '(While null value test) Alert: Your disk or data storage volume is full. sqlThread will now exit.'
                )
                shared.UISignalQueue.put(('alert', (
                    tr.translateText("MainWindow", "Disk full"),
                    tr.translateText(
                        "MainWindow",
                        'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'
                    ), True)))
                if shared.daemon:
                    os._exit(0)
                else:
                    return
            else:
                logger.error(err)

        # Let us check to see the last time we vaccumed the messages.dat file.
        # If it has been more than a month let's do it now.
        item = '''SELECT value FROM settings WHERE key='lastvacuumtime';'''
        parameters = ''
        self.cur.execute(item, parameters)
        queryreturn = self.cur.fetchall()
        for row in queryreturn:
            value, = row
            if int(value) < int(time.time()) - 2592000:
                logger.info(
                    'It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...'
                )
                try:
                    self.cur.execute(''' VACUUM ''')
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal(
                            '(While VACUUM) Alert: Your disk or data storage volume is full. sqlThread will now exit.'
                        )
                        shared.UISignalQueue.put(('alert', (
                            tr.translateText("MainWindow", "Disk full"),
                            tr.translateText(
                                "MainWindow",
                                'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'
                            ), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                item = '''update settings set value=? WHERE key='lastvacuumtime';'''
                parameters = (int(time.time()), )
                self.cur.execute(item, parameters)

        while True:
            item = shared.sqlSubmitQueue.get()
            if item == 'commit':
                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal(
                            '(While committing) Alert: Your disk or data storage volume is full. sqlThread will now exit.'
                        )
                        shared.UISignalQueue.put(('alert', (
                            tr.translateText("MainWindow", "Disk full"),
                            tr.translateText(
                                "MainWindow",
                                'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'
                            ), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
            elif item == 'exit':
                self.conn.close()
                logger.info('sqlThread exiting gracefully.')

                return
            elif item == 'movemessagstoprog':
                logger.debug(
                    'the sqlThread is moving the messages.dat file to the local program directory.'
                )

                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal(
                            '(while movemessagstoprog) Alert: Your disk or data storage volume is full. sqlThread will now exit.'
                        )
                        shared.UISignalQueue.put(('alert', (
                            tr.translateText("MainWindow", "Disk full"),
                            tr.translateText(
                                "MainWindow",
                                'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'
                            ), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                self.conn.close()
                shutil.move(shared.lookupAppdataFolder() + 'messages.dat',
                            'messages.dat')
                self.conn = sqlite3.connect('messages.dat')
                self.conn.text_factory = str
                self.cur = self.conn.cursor()
            elif item == 'movemessagstoappdata':
                logger.debug(
                    'the sqlThread is moving the messages.dat file to the Appdata folder.'
                )

                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal(
                            '(while movemessagstoappdata) Alert: Your disk or data storage volume is full. sqlThread will now exit.'
                        )
                        shared.UISignalQueue.put(('alert', (
                            tr.translateText("MainWindow", "Disk full"),
                            tr.translateText(
                                "MainWindow",
                                'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'
                            ), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                self.conn.close()
                shutil.move('messages.dat',
                            shared.lookupAppdataFolder() + 'messages.dat')
                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()
                try:
                    self.cur.execute(''' VACUUM ''')
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal(
                            '(while deleteandvacuume) Alert: Your disk or data storage volume is full. sqlThread will now exit.'
                        )
                        shared.UISignalQueue.put(('alert', (
                            tr.translateText("MainWindow", "Disk full"),
                            tr.translateText(
                                "MainWindow",
                                'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'
                            ), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
            else:
                parameters = shared.sqlSubmitQueue.get()
                # print 'item', item
                # print 'parameters', parameters
                try:
                    self.cur.execute(item, parameters)
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal(
                            '(while cur.execute) Alert: Your disk or data storage volume is full. sqlThread will now exit.'
                        )
                        shared.UISignalQueue.put(('alert', (
                            tr.translateText("MainWindow", "Disk full"),
                            tr.translateText(
                                "MainWindow",
                                'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'
                            ), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                    else:
                        logger.fatal(
                            'Major error occurred when trying to execute a SQL statement within the sqlThread. Please tell Atheros about this error message or post it in the forum! Error occurred while trying to execute statement: "%s"  Here are the parameters; you might want to censor this data with asterisks (***) as it can contain private information: %s. Here is the actual error message thrown by the sqlThread: %s',
                            str(item), str(repr(parameters)), str(err))
                        logger.fatal('This program shall now abruptly exit!')

                    os._exit(0)

                shared.sqlReturnQueue.put(self.cur.fetchall())
    def run(self):
        timeWeLastClearedInventoryAndPubkeysTables = 0
        try:
            shared.maximumLengthOfTimeToBotherResendingMessages = (float(shared.config.get('bitmessagesettings', 'stopresendingafterxdays')) * 24 * 60 * 60) + (float(shared.config.get('bitmessagesettings', 'stopresendingafterxmonths')) * (60 * 60 * 24 *365)/12)
        except:
            # Either the user hasn't set stopresendingafterxdays and stopresendingafterxmonths yet or the options are missing from the config file.
            shared.maximumLengthOfTimeToBotherResendingMessages = float('inf')

        while shared.shutdown == 0:
            shared.UISignalQueue.put((
                'updateStatusBar', 'Doing housekeeping (Flushing inventory in memory to disk...)'))
            with shared.inventoryLock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock.
                with SqlBulkExecute() as sql:
                    for hash, storedValue in shared.inventory.items():
                        objectType, streamNumber, payload, expiresTime, tag = storedValue
                        sql.execute(
                            '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''',
                            hash,
                            objectType,
                            streamNumber,
                            payload,
                            expiresTime,
                            tag)
                        del shared.inventory[hash]
            shared.UISignalQueue.put(('updateStatusBar', ''))
            
            shared.broadcastToSendDataQueues((
                0, 'pong', 'no data')) # commands the sendData threads to send out a pong message if they haven't sent anything else in the last five minutes. The socket timeout-time is 10 minutes.
            # If we are running as a daemon then we are going to fill up the UI
            # queue which will never be handled by a UI. We should clear it to
            # save memory.
            if shared.safeConfigGetBoolean('bitmessagesettings', 'daemon'):
                shared.UISignalQueue.queue.clear()
            if timeWeLastClearedInventoryAndPubkeysTables < int(time.time()) - 7380:
                timeWeLastClearedInventoryAndPubkeysTables = int(time.time())
                sqlExecute(
                    '''DELETE FROM inventory WHERE expirestime<? ''',
                    int(time.time()) - (60 * 60 * 3))
                # pubkeys
                sqlExecute(
                    '''DELETE FROM pubkeys WHERE time<? AND usedpersonally='no' ''',
                    int(time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys)

                # Let us resend getpubkey objects if we have not yet heard a pubkey, and also msg objects if we have not yet heard an acknowledgement
                queryreturn = sqlQuery(
                    '''select toaddress, ackdata, status FROM sent WHERE ((status='awaitingpubkey' OR status='msgsent') AND folder='sent' AND sleeptill<? AND senttime>?) ''',
                    int(time.time()),
                    int(time.time()) - shared.maximumLengthOfTimeToBotherResendingMessages)
                for row in queryreturn:
                    if len(row) < 2:
                        logger.error('Something went wrong in the singleCleaner thread: a query did not return the requested fields. ' + repr(row))
                        self.stop.wait(3)
                        break
                    toAddress, ackData, status = row
                    if status == 'awaitingpubkey':
                        resendPubkeyRequest(toAddress)
                    elif status == 'msgsent':
                        resendMsg(ackData)

                # Let's also clear and reload shared.inventorySets to keep it from
                # taking up an unnecessary amount of memory.
                for streamNumber in shared.inventorySets:
                    shared.inventorySets[streamNumber] = set()
                    queryData = sqlQuery('''SELECT hash FROM inventory WHERE streamnumber=?''', streamNumber)
                    for row in queryData:
                        shared.inventorySets[streamNumber].add(row[0])
                with shared.inventoryLock:
                    for hash, storedValue in shared.inventory.items():
                        objectType, streamNumber, payload, expiresTime, tag = storedValue
                        if not streamNumber in shared.inventorySets:
                            shared.inventorySets[streamNumber] = set()
                        shared.inventorySets[streamNumber].add(hash)

            # Let us write out the knowNodes to disk if there is anything new to write out.
            if shared.needToWriteKnownNodesToDisk:
                shared.knownNodesLock.acquire()
                output = open(shared.appdata + 'knownnodes.dat', 'wb')
                try:
                    pickle.dump(shared.knownNodes, output)
                    output.close()
                except Exception as err:
                    if "Errno 28" in str(err):
                        logger.fatal('(while receiveDataThread shared.needToWriteKnownNodesToDisk) Alert: Your disk or data storage volume is full. ')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                shared.knownNodesLock.release()
                shared.needToWriteKnownNodesToDisk = False
            self.stop.wait(300)
    def run(self):
        timeWeLastClearedInventoryAndPubkeysTables = 0

        while True:
            shared.UISignalQueue.put((
                'updateStatusBar', 'Doing housekeeping (Flushing inventory in memory to disk...)'))
            
            with shared.inventoryLock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock.
                with SqlBulkExecute() as sql:
                    for hash, storedValue in shared.inventory.items():
                        objectType, streamNumber, payload, receivedTime, tag = storedValue
                        if int(time.time()) - 3600 > receivedTime:
                            sql.execute(
                                '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''',
                                hash,
                                objectType,
                                streamNumber,
                                payload,
                                receivedTime,
                                tag)
                            del shared.inventory[hash]
            shared.UISignalQueue.put(('updateStatusBar', ''))
            shared.broadcastToSendDataQueues((
                0, 'pong', 'no data')) # commands the sendData threads to send out a pong message if they haven't sent anything else in the last five minutes. The socket timeout-time is 10 minutes.
            # If we are running as a daemon then we are going to fill up the UI
            # queue which will never be handled by a UI. We should clear it to
            # save memory.
            if shared.safeConfigGetBoolean('bitmessagesettings', 'daemon'):
                shared.UISignalQueue.queue.clear()
            if timeWeLastClearedInventoryAndPubkeysTables < int(time.time()) - 7380:
                timeWeLastClearedInventoryAndPubkeysTables = int(time.time())
                # inventory (moves data from the inventory data structure to
                # the on-disk sql database)
                # inventory (clears pubkeys after 28 days and everything else
                # after 2 days and 12 hours)
                sqlExecute(
                    '''DELETE FROM inventory WHERE (receivedtime<? AND objecttype<>'pubkey') OR (receivedtime<? AND objecttype='pubkey') ''',
                    int(time.time()) - shared.lengthOfTimeToLeaveObjectsInInventory,
                    int(time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys)

                # pubkeys
                sqlExecute(
                    '''DELETE FROM pubkeys WHERE time<? AND usedpersonally='no' ''',
                    int(time.time()) - shared.lengthOfTimeToHoldOnToAllPubkeys)

                queryreturn = sqlQuery(
                    '''select toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, status, pubkeyretrynumber, msgretrynumber FROM sent WHERE ((status='awaitingpubkey' OR status='msgsent') AND folder='sent') ''') # If the message's folder='trash' then we'll ignore it.
                for row in queryreturn:
                    if len(row) < 5:
                        with shared.printLock:
                            sys.stderr.write(
                                'Something went wrong in the singleCleaner thread: a query did not return the requested fields. ' + repr(row))
                        time.sleep(3)

                        break
                    toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, status, pubkeyretrynumber, msgretrynumber = row
                    if status == 'awaitingpubkey':#start:UI setting to stop trying to send messages after X hours/days/months
                        if int(shared.config.get('bitmessagesettings', 'timeperiod'))> -1:#The default value of timeperiod is -1.
                            if (int(time.time()) - lastactiontime) > (shared.maximumAgeOfAnObjectThatIAmWillingToAccept * (2 ** (pubkeyretrynumber))) and ((int(time.time()) - lastactiontime) < int(shared.config.get('bitmessagesettings', 'timeperiod'))):
                                resendPubkey(pubkeyretrynumber,toripe)#This will be executed if the user has adjusted the time period with some value
                        else:
                            if int(time.time()) - lastactiontime > (shared.maximumAgeOfAnObjectThatIAmWillingToAccept * (2 ** (pubkeyretrynumber))):
                                resendPubkey(pubkeyretrynumber,toripe)#This will be executed if the time period has its default value -1. Input (blank/blank/blank)
                    else: # status == msgsent
                        if int(shared.config.get('bitmessagesettings', 'timeperiod'))> -1:
                            if (int(time.time()) - lastactiontime) > (shared.maximumAgeOfAnObjectThatIAmWillingToAccept * (2 ** (msgretrynumber))) and ((int(time.time()) - lastactiontime) < int(shared.config.get('bitmessagesettings', 'timeperiod'))):
                                resendMsg(msgretrynumber,ackdata)
                        else:
                            if int(time.time()) - lastactiontime > (shared.maximumAgeOfAnObjectThatIAmWillingToAccept * (2 ** (msgretrynumber))):
                                resendMsg(msgretrynumber,ackdata)
                             #end      
                
                # Let's also clear and reload shared.inventorySets to keep it from
                # taking up an unnecessary amount of memory.
                for streamNumber in shared.inventorySets:
                    shared.inventorySets[streamNumber] = set()
                    queryData = sqlQuery('''SELECT hash FROM inventory WHERE streamnumber=?''', streamNumber)
                    for row in queryData:
                        shared.inventorySets[streamNumber].add(row[0])
                with shared.inventoryLock:
                    for hash, storedValue in shared.inventory.items():
                        objectType, streamNumber, payload, receivedTime, tag = storedValue
                        if streamNumber in shared.inventorySets:
                            shared.inventorySets[streamNumber].add(hash)

            # Let us write out the knowNodes to disk if there is anything new to write out.
            if shared.needToWriteKnownNodesToDisk:
                shared.knownNodesLock.acquire()
                output = open(shared.appdata + 'knownnodes.dat', 'wb')
                try:
                    pickle.dump(shared.knownNodes, output)
                    output.close()
                except Exception as err:
                    if "Errno 28" in str(err):
                        logger.fatal('(while receiveDataThread shared.needToWriteKnownNodesToDisk) Alert: Your disk or data storage volume is full. ')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                shared.knownNodesLock.release()
                shared.needToWriteKnownNodesToDisk = False
            time.sleep(300)
    def run(self):
        while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
            time.sleep(2)
        while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'):
            maximumConnections = 1 if shared.trustedPeer else 8 # maximum number of outgoing connections = 8
            while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections:
                time.sleep(10)
            if shared.shutdown:
                break
            random.seed()
            peer = self._getPeer()
            shared.alreadyAttemptedConnectionsListLock.acquire()
            while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
                shared.alreadyAttemptedConnectionsListLock.release()
                # print 'choosing new sample'
                random.seed()
                peer = self._getPeer()
                time.sleep(1)
                # Clear out the shared.alreadyAttemptedConnectionsList every half
                # hour so that this program will again attempt a connection
                # to any nodes, even ones it has already tried.
                if (time.time() - shared.alreadyAttemptedConnectionsListResetTime) > 1800:
                    shared.alreadyAttemptedConnectionsList.clear()
                    shared.alreadyAttemptedConnectionsListResetTime = int(
                        time.time())
                shared.alreadyAttemptedConnectionsListLock.acquire()
            shared.alreadyAttemptedConnectionsList[peer] = 0
            shared.alreadyAttemptedConnectionsListLock.release()
            if peer.host.find(':') == -1:
                address_family = socket.AF_INET
            else:
                address_family = socket.AF_INET6
            try:
                sock = socks.socksocket(address_family, socket.SOCK_STREAM)
            except:
                """
                The line can fail on Windows systems which aren't
                64-bit compatiable:
                      File "C:\Python27\lib\socket.py", line 187, in __init__
                        _sock = _realsocket(family, type, proto)
                      error: [Errno 10047] An address incompatible with the requested protocol was used
                      
                So let us remove the offending address from our knownNodes file.
                """
                shared.knownNodesLock.acquire()
                try:
                    del shared.knownNodes[self.streamNumber][peer]
                except:
                    pass
                shared.knownNodesLock.release()
                with shared.printLock:
                    print 'deleting ', peer, 'from shared.knownNodes because it caused a socks.socksocket exception. We must not be 64-bit compatible.'
                continue
            # This option apparently avoids the TIME_WAIT state so that we
            # can rebind faster
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.settimeout(20)
            if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2:
                with shared.printLock:
                    print 'Trying an outgoing connection to', peer

                # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS4a':
                if shared.verbose >= 2:
                    with shared.printLock:
                        print '(Using SOCKS4a) Trying an outgoing connection to', peer

                proxytype = socks.PROXY_TYPE_SOCKS4
                sockshostname = shared.config.get(
                    'bitmessagesettings', 'sockshostname')
                socksport = shared.config.getint(
                    'bitmessagesettings', 'socksport')
                rdns = True  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean('bitmessagesettings', 'socksauthentication'):
                    socksusername = shared.config.get(
                        'bitmessagesettings', 'socksusername')
                    sockspassword = shared.config.get(
                        'bitmessagesettings', 'sockspassword')
                    sock.setproxy(
                        proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
                else:
                    sock.setproxy(
                        proxytype, sockshostname, socksport, rdns)
            elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
                if shared.verbose >= 2:
                    with shared.printLock:
                        print '(Using SOCKS5) Trying an outgoing connection to', peer

                proxytype = socks.PROXY_TYPE_SOCKS5
                sockshostname = shared.config.get(
                    'bitmessagesettings', 'sockshostname')
                socksport = shared.config.getint(
                    'bitmessagesettings', 'socksport')
                rdns = True  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean('bitmessagesettings', 'socksauthentication'):
                    socksusername = shared.config.get(
                        'bitmessagesettings', 'socksusername')
                    sockspassword = shared.config.get(
                        'bitmessagesettings', 'sockspassword')
                    sock.setproxy(
                        proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
                else:
                    sock.setproxy(
                        proxytype, sockshostname, socksport, rdns)

            try:
                sock.connect((peer.host, peer.port))
                rd = receiveDataThread()
                rd.daemon = True  # close the main program even if there are threads left
                someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory.
                sendDataThreadQueue = Queue.Queue() # Used to submit information to the send data thread for this connection. 
                rd.setup(sock, 
                         peer.host, 
                         peer.port, 
                         self.streamNumber,
                         someObjectsOfWhichThisRemoteNodeIsAlreadyAware, 
                         self.selfInitiatedConnections, 
                         sendDataThreadQueue)
                rd.start()
                with shared.printLock:
                    print self, 'connected to', peer, 'during an outgoing attempt.'


                sd = sendDataThread(sendDataThreadQueue)
                sd.setup(sock, peer.host, peer.port, self.streamNumber,
                         someObjectsOfWhichThisRemoteNodeIsAlreadyAware)
                sd.start()
                sd.sendVersionMessage()

            except socks.GeneralProxyError as err:
                if shared.verbose >= 2:
                    with shared.printLock:
                        print 'Could NOT connect to', peer, 'during outgoing attempt.', err

                deletedPeer = None
                with shared.knownNodesLock:
                    """
                    It is remotely possible that peer is no longer in shared.knownNodes.
                    This could happen if two outgoingSynSender threads both try to 
                    connect to the same peer, both fail, and then both try to remove
                    it from shared.knownNodes. This is unlikely because of the
                    alreadyAttemptedConnectionsList but because we clear that list once
                    every half hour, it can happen.
                    """
                    if peer in shared.knownNodes[self.streamNumber]:
                        timeLastSeen = shared.knownNodes[self.streamNumber][peer]
                        if (int(time.time()) - timeLastSeen) > 172800 and len(shared.knownNodes[self.streamNumber]) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the shared.knownNodes data-structure.
                            del shared.knownNodes[self.streamNumber][peer]
                            deletedPeer = peer
                if deletedPeer:
                    with shared.printLock:
                        print 'deleting', peer, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.'

            except socks.Socks5AuthError as err:
                shared.UISignalQueue.put((
                    'updateStatusBar', tr.translateText(
                    "MainWindow", "SOCKS5 Authentication problem: %1").arg(str(err))))
            except socks.Socks5Error as err:
                pass
                print 'SOCKS5 error. (It is possible that the server wants authentication).)', str(err)
            except socks.Socks4Error as err:
                print 'Socks4Error:', err
            except socket.error as err:
                if shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS':
                    print 'Bitmessage MIGHT be having trouble connecting to the SOCKS server. ' + str(err)
                else:
                    if shared.verbose >= 1:
                        with shared.printLock:
                            print 'Could NOT connect to', peer, 'during outgoing attempt.', err

                deletedPeer = None
                with shared.knownNodesLock:
                    """
                    It is remotely possible that peer is no longer in shared.knownNodes.
                    This could happen if two outgoingSynSender threads both try to 
                    connect to the same peer, both fail, and then both try to remove
                    it from shared.knownNodes. This is unlikely because of the
                    alreadyAttemptedConnectionsList but because we clear that list once
                    every half hour, it can happen.
                    """
                    if peer in shared.knownNodes[self.streamNumber]:
                        timeLastSeen = shared.knownNodes[self.streamNumber][peer]
                        if (int(time.time()) - timeLastSeen) > 172800 and len(shared.knownNodes[self.streamNumber]) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the shared.knownNodes data-structure.
                            del shared.knownNodes[self.streamNumber][peer]
                            deletedPeer = peer
                if deletedPeer:
                    with shared.printLock:
                        print 'deleting', peer, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.'

            except Exception as err:
                sys.stderr.write(
                    'An exception has occurred in the outgoingSynSender thread that was not caught by other exception types: ')
                import traceback
                traceback.print_exc()
            time.sleep(0.1)
    def processmsg(self, data):
        messageProcessingStartTime = time.time()
        shared.numberOfMessagesProcessed += 1
        shared.UISignalQueue.put(
            ('updateNumberOfMessagesProcessed', 'no data'))
        readPosition = 20  # bypass the nonce, time, and object type
        msgVersion, msgVersionLength = decodeVarint(
            data[readPosition:readPosition + 9])
        if msgVersion != 1:
            logger.info(
                'Cannot understand message versions other than one. Ignoring message.'
            )
            return
        readPosition += msgVersionLength

        streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = decodeVarint(
            data[readPosition:readPosition + 9])
        readPosition += streamNumberAsClaimedByMsgLength
        inventoryHash = calculateInventoryHash(data)
        initialDecryptionSuccessful = False
        # Let's check whether this is a message acknowledgement bound for us.
        if data[-32:] in shared.ackdataForWhichImWatching:
            logger.info('This msg IS an acknowledgement bound for me.')
            del shared.ackdataForWhichImWatching[data[-32:]]
            sqlExecute('UPDATE sent SET status=? WHERE ackdata=?',
                       'ackreceived', data[-32:])
            shared.UISignalQueue.put(
                ('updateSentItemStatusByAckdata',
                 (data[-32:],
                  tr.translateText(
                      "MainWindow",
                      'Acknowledgement of the message received. %1').arg(
                          l10n.formatTimestamp()))))
            return
        else:
            logger.info('This was NOT an acknowledgement bound for me.')

        # This is not an acknowledgement bound for me. See if it is a message
        # bound for me by trying to decrypt it with my private keys.

        for key, cryptorObject in shared.myECCryptorObjects.items():
            try:
                decryptedData = cryptorObject.decrypt(data[readPosition:])
                toRipe = key  # This is the RIPE hash of my pubkeys. We need this below to compare to the destination_ripe included in the encrypted data.
                initialDecryptionSuccessful = True
                logger.info(
                    'EC decryption successful using key associated with ripe hash: %s.'
                    % key.encode('hex'))
                break
            except Exception as err:
                pass
        if not initialDecryptionSuccessful:
            # This is not a message bound for me.
            logger.info(
                'Length of time program spent failing to decrypt this message: %s seconds.'
                % (time.time() - messageProcessingStartTime, ))
            return

        # This is a message bound for me.
        toAddress = shared.myAddressesByHash[
            toRipe]  # Look up my address based on the RIPE hash.
        readPosition = 0
        sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += sendersAddressVersionNumberLength
        if sendersAddressVersionNumber == 0:
            logger.info(
                'Cannot understand sendersAddressVersionNumber = 0. Ignoring message.'
            )
            return
        if sendersAddressVersionNumber > 4:
            logger.info(
                'Sender\'s address version number %s not yet supported. Ignoring message.'
                % sendersAddressVersionNumber)
            return
        if len(decryptedData) < 170:
            logger.info(
                'Length of the unencrypted data is unreasonably short. Sanity check failed. Ignoring message.'
            )
            return
        sendersStreamNumber, sendersStreamNumberLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        if sendersStreamNumber == 0:
            logger.info('sender\'s stream number is 0. Ignoring message.')
            return
        readPosition += sendersStreamNumberLength
        behaviorBitfield = decryptedData[readPosition:readPosition + 4]
        readPosition += 4
        pubSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64]
        readPosition += 64
        pubEncryptionKey = '\x04' + decryptedData[readPosition:readPosition +
                                                  64]
        readPosition += 64
        if sendersAddressVersionNumber >= 3:
            requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(
                decryptedData[readPosition:readPosition + 10])
            readPosition += varintLength
            logger.info(
                'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s'
                % requiredAverageProofOfWorkNonceTrialsPerByte)
            requiredPayloadLengthExtraBytes, varintLength = decodeVarint(
                decryptedData[readPosition:readPosition + 10])
            readPosition += varintLength
            logger.info('sender\'s requiredPayloadLengthExtraBytes is %s' %
                        requiredPayloadLengthExtraBytes)
        endOfThePublicKeyPosition = readPosition  # needed for when we store the pubkey in our database of pubkeys for later use.
        if toRipe != decryptedData[readPosition:readPosition + 20]:
            logger.info(
                'The original sender of this message did not send it to you. Someone is attempting a Surreptitious Forwarding Attack.\n\
                See: http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html \n\
                your toRipe: %s\n\
                embedded destination toRipe: %s' %
                (toRipe.encode('hex'),
                 decryptedData[readPosition:readPosition + 20].encode('hex')))
            return
        readPosition += 20
        messageEncodingType, messageEncodingTypeLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += messageEncodingTypeLength
        messageLength, messageLengthLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += messageLengthLength
        message = decryptedData[readPosition:readPosition + messageLength]
        # print 'First 150 characters of message:', repr(message[:150])
        readPosition += messageLength
        ackLength, ackLengthLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += ackLengthLength
        ackData = decryptedData[readPosition:readPosition + ackLength]
        readPosition += ackLength
        positionOfBottomOfAckData = readPosition  # needed to mark the end of what is covered by the signature
        signatureLength, signatureLengthLength = decodeVarint(
            decryptedData[readPosition:readPosition + 10])
        readPosition += signatureLengthLength
        signature = decryptedData[readPosition:readPosition + signatureLength]
        signedData = data[8:20] + encodeVarint(
            1) + encodeVarint(streamNumberAsClaimedByMsg
                              ) + decryptedData[:positionOfBottomOfAckData]

        if not highlevelcrypto.verify(signedData, signature,
                                      pubSigningKey.encode('hex')):
            logger.debug('ECDSA verify failed')
            return
        logger.debug('ECDSA verify passed')
        logger.debug(
            'As a matter of intellectual curiosity, here is the Bitcoin address associated with the keys owned by the other person: %s  ..and here is the testnet address: %s. The other person must take their private signing key from Bitmessage and import it into Bitcoin (or a service like Blockchain.info) for it to be of any use. Do not use this unless you know what you are doing.'
            %
            (helper_bitcoin.calculateBitcoinAddressFromPubkey(pubSigningKey),
             helper_bitcoin.calculateTestnetAddressFromPubkey(pubSigningKey)))

        # calculate the fromRipe.
        sha = hashlib.new('sha512')
        sha.update(pubSigningKey + pubEncryptionKey)
        ripe = hashlib.new('ripemd160')
        ripe.update(sha.digest())
        fromAddress = encodeAddress(sendersAddressVersionNumber,
                                    sendersStreamNumber, ripe.digest())

        # Let's store the public key in case we want to reply to this
        # person.
        sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', ripe.digest(),
                   sendersAddressVersionNumber,
                   decryptedData[:endOfThePublicKeyPosition], int(time.time()),
                   'yes')

        # 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.
        if sendersAddressVersionNumber <= 3:
            self.possibleNewPubkey(ripe=ripe.digest())
        elif sendersAddressVersionNumber >= 4:
            self.possibleNewPubkey(address=fromAddress)

        # If this message is bound for one of my version 3 addresses (or
        # higher), then we must check to make sure it meets our demanded
        # proof of work requirement. If this is bound for one of my chan
        # addresses then we skip this check; the minimum network POW is
        # fine.
        if decodeAddress(toAddress)[1] >= 3 and not shared.safeConfigGetBoolean(
                toAddress, 'chan'
        ):  # If the toAddress version number is 3 or higher and not one of my chan addresses:
            if not shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(
                    fromAddress):  # If I'm not friendly with this person:
                requiredNonceTrialsPerByte = shared.config.getint(
                    toAddress, 'noncetrialsperbyte')
                requiredPayloadLengthExtraBytes = shared.config.getint(
                    toAddress, 'payloadlengthextrabytes')
                if not shared.isProofOfWorkSufficient(
                        data, requiredNonceTrialsPerByte,
                        requiredPayloadLengthExtraBytes):
                    logger.info(
                        'Proof of work in msg is insufficient only because it does not meet our higher requirement.'
                    )
                    return
        blockMessage = False  # Gets set to True if the user shouldn't see the message according to black or white lists.
        if shared.config.get(
                'bitmessagesettings',
                'blackwhitelist') == 'black':  # If we are using a blacklist
            queryreturn = sqlQuery(
                '''SELECT label FROM blacklist where address=? and enabled='1' ''',
                fromAddress)
            if queryreturn != []:
                logger.info('Message ignored because address is in blacklist.')

                blockMessage = True
        else:  # We're using a whitelist
            queryreturn = sqlQuery(
                '''SELECT label FROM whitelist where address=? and enabled='1' ''',
                fromAddress)
            if queryreturn == []:
                logger.info(
                    'Message ignored because address not in whitelist.')
                blockMessage = True

        toLabel = shared.config.get(toAddress, 'label')
        if toLabel == '':
            toLabel = toAddress

        if messageEncodingType == 2:
            subject, body = self.decodeType2Message(message)
            logger.info('Message subject (first 100 characters): %s' %
                        repr(subject)[:100])
        elif messageEncodingType == 1:
            body = message
            subject = ''
        elif messageEncodingType == 0:
            logger.info(
                'messageEncodingType == 0. Doing nothing with the message. They probably just sent it so that we would store their public key or send their ack data for them.'
            )
            subject = ''
            body = ''
        else:
            body = 'Unknown encoding type.\n\n' + repr(message)
            subject = ''
        # Let us make sure that we haven't already received this message
        if helper_inbox.isMessageAlreadyInInbox(toAddress, fromAddress,
                                                subject, body,
                                                messageEncodingType):
            logger.info('This msg is already in our inbox. Ignoring it.')
            blockMessage = True
        if not blockMessage:
            if messageEncodingType != 0:
                t = (inventoryHash, toAddress, fromAddress, subject,
                     int(time.time()), body, 'inbox', messageEncodingType, 0)
                helper_inbox.insert(t)

                shared.UISignalQueue.put(
                    ('displayNewInboxMessage', (inventoryHash, toAddress,
                                                fromAddress, subject, body)))

            # If we are behaving as an API then we might need to run an
            # outside command to let some program know that a new message
            # has arrived.
            if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'):
                try:
                    apiNotifyPath = shared.config.get('bitmessagesettings',
                                                      'apinotifypath')
                except:
                    apiNotifyPath = ''
                if apiNotifyPath != '':
                    call([apiNotifyPath, "newMessage"])

            # Let us now check and see whether our receiving address is
            # behaving as a mailing list
            if shared.safeConfigGetBoolean(toAddress, 'mailinglist'):
                try:
                    mailingListName = shared.config.get(
                        toAddress, 'mailinglistname')
                except:
                    mailingListName = ''
                # Let us send out this message as a broadcast
                subject = self.addMailingListNameToSubject(
                    subject, mailingListName)
                # Let us now send this message out as a broadcast
                message = time.strftime(
                    "%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime()
                ) + '   Message ostensibly from ' + fromAddress + ':\n\n' + body
                fromAddress = toAddress  # The fromAddress for the broadcast that we are about to send is the toAddress (my address) for the msg message we are currently processing.
                ackdataForBroadcast = OpenSSL.rand(
                    32
                )  # We don't actually need the ackdataForBroadcast for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating.
                toAddress = '[Broadcast subscribers]'
                ripe = ''

                t = ('', toAddress, ripe,
                     fromAddress, subject, message, ackdataForBroadcast,
                     int(time.time()), 'broadcastqueued', 1, 1, 'sent', 2)
                helper_sent.insert(t)

                shared.UISignalQueue.put(
                    ('displayNewSentMessage',
                     (toAddress, '[Broadcast subscribers]', fromAddress,
                      subject, message, ackdataForBroadcast)))
                shared.workerQueue.put(('sendbroadcast', ''))

        if self.ackDataHasAVaildHeader(ackData):
            shared.checkAndShareObjectWithPeers(ackData[24:])

        # Display timing data
        timeRequiredToAttemptToDecryptMessage = time.time(
        ) - messageProcessingStartTime
        shared.successfullyDecryptMessageTimings.append(
            timeRequiredToAttemptToDecryptMessage)
        sum = 0
        for item in shared.successfullyDecryptMessageTimings:
            sum += item
        logger.debug('Time to decrypt this message successfully: %s\n\
                     Average time for all message decryption successes since startup: %s.'
                     % (timeRequiredToAttemptToDecryptMessage,
                        sum / len(shared.successfullyDecryptMessageTimings)))
Exemple #26
0
    def run(self):        
        self.conn = sqlite3.connect(shared.appdata + 'messages.dat')
        self.conn.text_factory = str
        self.cur = self.conn.cursor()

        try:
            self.cur.execute(
                '''CREATE TABLE inbox (msgid blob, toaddress text, fromaddress text, subject text, received text, message text, folder text, encodingtype int, read bool, sighash blob, UNIQUE(msgid) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''CREATE TABLE sent (msgid blob, toaddress text, toripe blob, fromaddress text, subject text, message text, ackdata blob, senttime integer, lastactiontime integer, sleeptill integer, status text, retrynumber integer, folder text, encodingtype int, ttl int)''' )
            self.cur.execute(
                '''CREATE TABLE subscriptions (label text, address text, enabled bool)''' )
            self.cur.execute(
                '''CREATE TABLE addressbook (label text, address text)''' )
            self.cur.execute(
                '''CREATE TABLE blacklist (label text, address text, enabled bool)''' )
            self.cur.execute(
                '''CREATE TABLE whitelist (label text, address text, enabled bool)''' )
            self.cur.execute(
                '''CREATE TABLE pubkeys (address text, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(address) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''CREATE TABLE inventory (hash blob, objecttype int, streamnumber int, payload blob, expirestime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''')
            self.cur.execute(
                '''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' )
            self.cur.execute( '''INSERT INTO settings VALUES('version','10')''')
            self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', (
                int(time.time()),))
            self.cur.execute(
                '''CREATE TABLE objectprocessorqueue (objecttype int, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)''' )
            self.conn.commit()
            logger.info('Created messages database file')
        except Exception as err:
            if str(err) == 'table inbox already exists':
                logger.debug('Database file already exists.')

            else:
                sys.stderr.write(
                    'ERROR trying to create database file (message.dat). Error message: %s\n' % str(err))
                os._exit(0)

        if shared.config.getint('bitmessagesettings', 'settingsversion') == 1:
            shared.config.set('bitmessagesettings', 'settingsversion', '2')
                      # If the settings version is equal to 2 or 3 then the
                      # sqlThread will modify the pubkeys table and change
                      # the settings version to 4.
            shared.config.set('bitmessagesettings', 'socksproxytype', 'none')
            shared.config.set('bitmessagesettings', 'sockshostname', 'localhost')
            shared.config.set('bitmessagesettings', 'socksport', '9050')
            shared.config.set('bitmessagesettings', 'socksauthentication', 'false')
            shared.config.set('bitmessagesettings', 'socksusername', '')
            shared.config.set('bitmessagesettings', 'sockspassword', '')
            shared.config.set('bitmessagesettings', 'sockslisten', 'false')
            shared.config.set('bitmessagesettings', 'keysencrypted', 'false')
            shared.config.set('bitmessagesettings', 'messagesencrypted', 'false')

        # People running earlier versions of PyBitmessage do not have the
        # usedpersonally field in their pubkeys table. Let's add it.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 2:
            item = '''ALTER TABLE pubkeys ADD usedpersonally text DEFAULT 'no' '''
            parameters = ''
            self.cur.execute(item, parameters)
            self.conn.commit()

            shared.config.set('bitmessagesettings', 'settingsversion', '3')

        # People running earlier versions of PyBitmessage do not have the
        # encodingtype field in their inbox and sent tables or the read field
        # in the inbox table. Let's add them.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 3:
            item = '''ALTER TABLE inbox ADD encodingtype int DEFAULT '2' '''
            parameters = ''
            self.cur.execute(item, parameters)

            item = '''ALTER TABLE inbox ADD read bool DEFAULT '1' '''
            parameters = ''
            self.cur.execute(item, parameters)

            item = '''ALTER TABLE sent ADD encodingtype int DEFAULT '2' '''
            parameters = ''
            self.cur.execute(item, parameters)
            self.conn.commit()

            shared.config.set('bitmessagesettings', 'settingsversion', '4')

        if shared.config.getint('bitmessagesettings', 'settingsversion') == 4:
            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')

        # From now on, let us keep a 'version' embedded in the messages.dat
        # file so that when we make changes to the database, the database
        # version we are on can stay embedded in the messages.dat file. Let us
        # check to see if the settings table exists yet.
        item = '''SELECT name FROM sqlite_master WHERE type='table' AND name='settings';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if self.cur.fetchall() == []:
            # The settings table doesn't exist. We need to make it.
            logger.debug('In messages.dat database, creating new \'settings\' table.')
            self.cur.execute(
                '''CREATE TABLE settings (key text, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' )
            self.cur.execute( '''INSERT INTO settings VALUES('version','1')''')
            self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', (
                int(time.time()),))
            logger.debug('In messages.dat database, removing an obsolete field from the pubkeys table.')
            self.cur.execute(
                '''CREATE TEMPORARY TABLE pubkeys_backup(hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE);''')
            self.cur.execute(
                '''INSERT INTO pubkeys_backup SELECT hash, transmitdata, time, usedpersonally FROM pubkeys;''')
            self.cur.execute( '''DROP TABLE pubkeys''')
            self.cur.execute(
                '''CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''INSERT INTO pubkeys SELECT hash, transmitdata, time, usedpersonally FROM pubkeys_backup;''')
            self.cur.execute( '''DROP TABLE pubkeys_backup;''')
            logger.debug('Deleting all pubkeys from inventory. They will be redownloaded and then saved with the correct times.')
            self.cur.execute(
                '''delete from inventory where objecttype = 'pubkey';''')
            logger.debug('replacing Bitmessage announcements mailing list with a new one.')
            self.cur.execute(
                '''delete from subscriptions where address='BM-BbkPSZbzPwpVcYZpU4yHwf9ZPEapN5Zx' ''')
            self.cur.execute(
                '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''')
            logger.debug('Commiting.')
            self.conn.commit()
            logger.debug('Vacuuming message.dat. You might notice that the file size gets much smaller.')
            self.cur.execute( ''' VACUUM ''')

        # After code refactoring, the possible status values for sent messages
        # have changed.
        self.cur.execute(
            '''update sent set status='doingmsgpow' where status='doingpow'  ''')
        self.cur.execute(
            '''update sent set status='msgsent' where status='sentmessage'  ''')
        self.cur.execute(
            '''update sent set status='doingpubkeypow' where status='findingpubkey'  ''')
        self.cur.execute(
            '''update sent set status='broadcastqueued' where status='broadcastpending'  ''')
        self.conn.commit()
        
        if not shared.config.has_option('bitmessagesettings', 'sockslisten'):
            shared.config.set('bitmessagesettings', 'sockslisten', 'false')
            
        ensureNamecoinOptions()
            
        """# Add a new column to the inventory table to store the first 20 bytes of encrypted messages to support Android app
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if int(self.cur.fetchall()[0][0]) == 1:
            print 'upgrading database'
            item = '''ALTER TABLE inventory ADD first20bytesofencryptedmessage blob DEFAULT '' '''
            parameters = ''
            self.cur.execute(item, parameters)
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (2,)
            self.cur.execute(item, parameters)"""

        # Let's get rid of the first20bytesofencryptedmessage field in the inventory table.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        if int(self.cur.fetchall()[0][0]) == 2:
            logger.debug('In messages.dat database, removing an obsolete field from the inventory table.')
            self.cur.execute(
                '''CREATE TEMPORARY TABLE inventory_backup(hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE);''')
            self.cur.execute(
                '''INSERT INTO inventory_backup SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory;''')
            self.cur.execute( '''DROP TABLE inventory''')
            self.cur.execute(
                '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''INSERT INTO inventory SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory_backup;''')
            self.cur.execute( '''DROP TABLE inventory_backup;''')
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (3,)
            self.cur.execute(item, parameters)

        # Add a new column to the inventory table to store tags.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 1 or currentVersion == 3:
            logger.debug('In messages.dat database, adding tag field to the inventory table.')
            item = '''ALTER TABLE inventory ADD tag blob DEFAULT '' '''
            parameters = ''
            self.cur.execute(item, parameters)
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (4,)
            self.cur.execute(item, parameters)

        if not shared.config.has_option('bitmessagesettings', 'userlocale'):
            shared.config.set('bitmessagesettings', 'userlocale', 'system')
        if not shared.config.has_option('bitmessagesettings', 'sendoutgoingconnections'):
            shared.config.set('bitmessagesettings', 'sendoutgoingconnections', 'True')

        # Raise the default required difficulty from 1 to 2
        # With the change to protocol v3, this is obsolete.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 6:
            """if int(shared.config.get('bitmessagesettings','defaultnoncetrialsperbyte')) == shared.networkDefaultProofOfWorkNonceTrialsPerByte:
                shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte', str(shared.networkDefaultProofOfWorkNonceTrialsPerByte * 2))
                """
            shared.config.set('bitmessagesettings', 'settingsversion', '7')

        # Add a new column to the pubkeys table to store the address version.
        # We're going to trash all of our pubkeys and let them be redownloaded.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 4:
            self.cur.execute( '''DROP TABLE pubkeys''')
            self.cur.execute(
                '''CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''delete from inventory where objecttype = 'pubkey';''')
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (5,)
            self.cur.execute(item, parameters)
            
        if not shared.config.has_option('bitmessagesettings', 'useidenticons'):
            shared.config.set('bitmessagesettings', 'useidenticons', 'True')
        if not shared.config.has_option('bitmessagesettings', 'identiconsuffix'): # acts as a salt
            shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons

        #Add settings to support no longer resending messages after a certain period of time even if we never get an ack
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 7:
            shared.config.set(
                'bitmessagesettings', 'stopresendingafterxdays', '')
            shared.config.set(
                'bitmessagesettings', 'stopresendingafterxmonths', '')
            shared.config.set('bitmessagesettings', 'settingsversion', '8') 

        # Add a new table: objectprocessorqueue with which to hold objects
        # that have yet to be processed if the user shuts down Bitmessage.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 5:
            self.cur.execute( '''DROP TABLE knownnodes''')
            self.cur.execute(
                '''CREATE TABLE objectprocessorqueue (objecttype text, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)''' )
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (6,)
            self.cur.execute(item, parameters)
        
        # changes related to protocol v3
		#    In table inventory and objectprocessorqueue, objecttype is now an integer (it was a human-friendly string previously)
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 6:
            logger.debug('In messages.dat database, dropping and recreating the inventory table.')
            self.cur.execute( '''DROP TABLE inventory''')
            self.cur.execute( '''CREATE TABLE inventory (hash blob, objecttype int, streamnumber int, payload blob, expirestime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)''' )
            self.cur.execute( '''DROP TABLE objectprocessorqueue''')
            self.cur.execute( '''CREATE TABLE objectprocessorqueue (objecttype int, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)''' )
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (7,)
            self.cur.execute(item, parameters)
            logger.debug('Finished dropping and recreating the inventory table.')
        
        # With the change to protocol version 3, reset the user-settable difficulties to 1    
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 8:
            shared.config.set('bitmessagesettings','defaultnoncetrialsperbyte', str(shared.networkDefaultProofOfWorkNonceTrialsPerByte))
            shared.config.set('bitmessagesettings','defaultpayloadlengthextrabytes', str(shared.networkDefaultPayloadLengthExtraBytes))
            previousTotalDifficulty = int(shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte')) / 320
            previousSmallMessageDifficulty = int(shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')) / 14000
            shared.config.set('bitmessagesettings','maxacceptablenoncetrialsperbyte', str(previousTotalDifficulty * 1000))
            shared.config.set('bitmessagesettings','maxacceptablepayloadlengthextrabytes', str(previousSmallMessageDifficulty * 1000))
            shared.config.set('bitmessagesettings', 'settingsversion', '9')
                
        # Adjust the required POW values for each of this user's addresses to conform to protocol v3 norms.
        if shared.config.getint('bitmessagesettings', 'settingsversion') == 9:
            for addressInKeysFile in shared.config.sections():
                try:
                    previousTotalDifficulty = float(shared.config.getint(addressInKeysFile, 'noncetrialsperbyte')) / 320
                    previousSmallMessageDifficulty = float(shared.config.getint(addressInKeysFile, 'payloadlengthextrabytes')) / 14000
                    if previousTotalDifficulty <= 2:
                        previousTotalDifficulty = 1
                    if previousSmallMessageDifficulty < 1:
                        previousSmallMessageDifficulty = 1
                    shared.config.set(addressInKeysFile,'noncetrialsperbyte', str(int(previousTotalDifficulty * 1000)))
                    shared.config.set(addressInKeysFile,'payloadlengthextrabytes', str(int(previousSmallMessageDifficulty * 1000)))
                except:
                    continue
            shared.config.set('bitmessagesettings', 'maxdownloadrate', '0')
            shared.config.set('bitmessagesettings', 'maxuploadrate', '0')
            shared.config.set('bitmessagesettings', 'settingsversion', '10')
            shared.writeKeysFile()                

        # The format of data stored in the pubkeys table has changed. Let's
        # clear it, and the pubkeys from inventory, so that they'll be re-downloaded.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 7:
            logger.debug('In messages.dat database, clearing pubkeys table because the data format has been updated.')
            self.cur.execute(
                '''delete from inventory where objecttype = 1;''')
            self.cur.execute(
                '''delete from pubkeys;''')
            # Any sending messages for which we *thought* that we had the pubkey must
            # be rechecked.
            self.cur.execute(
                '''UPDATE sent SET status='msgqueued' WHERE status='doingmsgpow' or status='badkey';''')
            query = '''update settings set value=? WHERE key='version';'''
            parameters = (8,)
            self.cur.execute(query, parameters)
            logger.debug('Finished clearing currently held pubkeys.')

        # Add a new column to the inbox table to store the hash of the message signature.
        # We'll use this as temporary message UUID in order to detect duplicates. 
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 8:
            logger.debug('In messages.dat database, adding sighash field to the inbox table.')
            item = '''ALTER TABLE inbox ADD sighash blob DEFAULT '' '''
            parameters = ''
            self.cur.execute(item, parameters)
            item = '''update settings set value=? WHERE key='version';'''
            parameters = (9,)
            self.cur.execute(item, parameters)
            
        # TTL is now user-specifiable. Let's add an option to save whatever the user selects. 
        if not shared.config.has_option('bitmessagesettings', 'ttl'):
            shared.config.set('bitmessagesettings', 'ttl', '367200')
        # We'll also need a `sleeptill` field and a `ttl` field. Also we can combine 
        # the pubkeyretrynumber and msgretrynumber into one.
        item = '''SELECT value FROM settings WHERE key='version';'''
        parameters = ''
        self.cur.execute(item, parameters)
        currentVersion = int(self.cur.fetchall()[0][0])
        if currentVersion == 9:
            logger.info('In messages.dat database, making TTL-related changes: combining the pubkeyretrynumber and msgretrynumber fields into the retrynumber field and adding the sleeptill and ttl fields...')
            self.cur.execute(
                '''CREATE TEMPORARY TABLE sent_backup (msgid blob, toaddress text, toripe blob, fromaddress text, subject text, message text, ackdata blob, lastactiontime integer, status text, retrynumber integer, folder text, encodingtype int)''' )
            self.cur.execute(
                '''INSERT INTO sent_backup SELECT msgid, toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, status, 0, folder, encodingtype FROM sent;''')
            self.cur.execute( '''DROP TABLE sent''')
            self.cur.execute(
                '''CREATE TABLE sent (msgid blob, toaddress text, toripe blob, fromaddress text, subject text, message text, ackdata blob, senttime integer, lastactiontime integer, sleeptill int, status text, retrynumber integer, folder text, encodingtype int, ttl int)''' )
            self.cur.execute(
                '''INSERT INTO sent SELECT msgid, toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, lastactiontime, 0, status, 0, folder, encodingtype, 216000 FROM sent_backup;''')
            self.cur.execute( '''DROP TABLE sent_backup''')
            logger.info('In messages.dat database, finished making TTL-related changes.')
            logger.debug('In messages.dat database, adding address field to the pubkeys table.')
            # We're going to have to calculate the address for each row in the pubkeys
            # table. Then we can take out the hash field. 
            self.cur.execute('''ALTER TABLE pubkeys ADD address text DEFAULT '' ''')
            self.cur.execute('''SELECT hash, addressversion FROM pubkeys''')
            queryResult = self.cur.fetchall()
            from addresses import encodeAddress
            for row in queryResult:
                hash, addressVersion = row
                address = encodeAddress(addressVersion, 1, hash)
                item = '''UPDATE pubkeys SET address=? WHERE hash=?;'''
                parameters = (address, hash)
                self.cur.execute(item, parameters)
            # Now we can remove the hash field from the pubkeys table.
            self.cur.execute(
                '''CREATE TEMPORARY TABLE pubkeys_backup (address text, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(address) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''INSERT INTO pubkeys_backup SELECT address, addressversion, transmitdata, time, usedpersonally FROM pubkeys;''')
            self.cur.execute( '''DROP TABLE pubkeys''')
            self.cur.execute(
                '''CREATE TABLE pubkeys (address text, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(address) ON CONFLICT REPLACE)''' )
            self.cur.execute(
                '''INSERT INTO pubkeys SELECT address, addressversion, transmitdata, time, usedpersonally FROM pubkeys_backup;''')
            self.cur.execute( '''DROP TABLE pubkeys_backup''')
            logger.debug('In messages.dat database, done adding address field to the pubkeys table and removing the hash field.')
            self.cur.execute('''update settings set value=10 WHERE key='version';''')
        
        
        # Are you hoping to add a new option to the keys.dat file of existing
        # Bitmessage users or modify the SQLite database? Add it right above this line!
        
        try:
            testpayload = '\x00\x00'
            t = ('1234', 1, testpayload, '12345678', 'no')
            self.cur.execute( '''INSERT INTO pubkeys VALUES(?,?,?,?,?)''', t)
            self.conn.commit()
            self.cur.execute(
                '''SELECT transmitdata FROM pubkeys WHERE address='1234' ''')
            queryreturn = self.cur.fetchall()
            for row in queryreturn:
                transmitdata, = row
            self.cur.execute('''DELETE FROM pubkeys WHERE address='1234' ''')
            self.conn.commit()
            if transmitdata == '':
                logger.fatal('Problem: The version of SQLite you have cannot store Null values. Please download and install the latest revision of your version of Python (for example, the latest Python 2.7 revision) and try again.\n')
                logger.fatal('PyBitmessage will now exit very abruptly. You may now see threading errors related to this abrupt exit but the problem you need to solve is related to SQLite.\n\n')
                os._exit(0)
        except Exception as err:
            if str(err) == 'database or disk is full':
                logger.fatal('(While null value test) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                if shared.daemon:
                    os._exit(0)
                else:
                    return
            else:
                logger.error(err)

        # Let us check to see the last time we vaccumed the messages.dat file.
        # If it has been more than a month let's do it now.
        item = '''SELECT value FROM settings WHERE key='lastvacuumtime';'''
        parameters = ''
        self.cur.execute(item, parameters)
        queryreturn = self.cur.fetchall()
        for row in queryreturn:
            value, = row
            if int(value) < int(time.time()) - 86400:
                logger.info('It has been a long time since the messages.dat file has been vacuumed. Vacuuming now...')
                try:
                    self.cur.execute( ''' VACUUM ''')
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(While VACUUM) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                item = '''update settings set value=? WHERE key='lastvacuumtime';'''
                parameters = (int(time.time()),)
                self.cur.execute(item, parameters)

        while True:
            item = shared.sqlSubmitQueue.get()
            if item == 'commit':
                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(While committing) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
            elif item == 'exit':
                self.conn.close()
                logger.info('sqlThread exiting gracefully.')

                return
            elif item == 'movemessagstoprog':
                logger.debug('the sqlThread is moving the messages.dat file to the local program directory.')

                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(while movemessagstoprog) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                self.conn.close()
                shutil.move(
                    shared.lookupAppdataFolder() + 'messages.dat', 'messages.dat')
                self.conn = sqlite3.connect('messages.dat')
                self.conn.text_factory = str
                self.cur = self.conn.cursor()
            elif item == 'movemessagstoappdata':
                logger.debug('the sqlThread is moving the messages.dat file to the Appdata folder.')

                try:
                    self.conn.commit()
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(while movemessagstoappdata) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                self.conn.close()
                shutil.move(
                    'messages.dat', shared.lookupAppdataFolder() + 'messages.dat')
                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()
                try:
                    self.cur.execute( ''' VACUUM ''')
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(while deleteandvacuume) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
            else:
                parameters = shared.sqlSubmitQueue.get()
                # print 'item', item
                # print 'parameters', parameters
                try:
                    self.cur.execute(item, parameters)
                except Exception as err:
                    if str(err) == 'database or disk is full':
                        logger.fatal('(while cur.execute) Alert: Your disk or data storage volume is full. sqlThread will now exit.')
                        shared.UISignalQueue.put(('alert', (tr.translateText("MainWindow", "Disk full"), tr.translateText("MainWindow", 'Alert: Your disk or data storage volume is full. Bitmessage will now exit.'), True)))
                        if shared.daemon:
                            os._exit(0)
                        else:
                            return
                    else:
                        logger.fatal('Major error occurred when trying to execute a SQL statement within the sqlThread. Please tell Atheros about this error message or post it in the forum! Error occurred while trying to execute statement: "%s"  Here are the parameters; you might want to censor this data with asterisks (***) as it can contain private information: %s. Here is the actual error message thrown by the sqlThread: %s', str(item), str(repr(parameters)),  str(err))
                        logger.fatal('This program shall now abruptly exit!')

                    os._exit(0)

                shared.sqlReturnQueue.put(self.cur.fetchall())
    def run(self):
        while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
            time.sleep(2)
        while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'):
            while len(self.selfInitiatedConnections[self.streamNumber]) >= 8:  # maximum number of outgoing connections = 8
                time.sleep(10)
            if shared.shutdown:
                break
            random.seed()
            shared.knownNodesLock.acquire()
            peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
            shared.knownNodesLock.release()
            shared.alreadyAttemptedConnectionsListLock.acquire()
            while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
                shared.alreadyAttemptedConnectionsListLock.release()
                # print 'choosing new sample'
                random.seed()
                shared.knownNodesLock.acquire()
                peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
                shared.knownNodesLock.release()
                time.sleep(1)
                # Clear out the shared.alreadyAttemptedConnectionsList every half
                # hour so that this program will again attempt a connection
                # to any nodes, even ones it has already tried.
                if (time.time() - shared.alreadyAttemptedConnectionsListResetTime) > 1800:
                    shared.alreadyAttemptedConnectionsList.clear()
                    shared.alreadyAttemptedConnectionsListResetTime = int(
                        time.time())
                shared.alreadyAttemptedConnectionsListLock.acquire()
            shared.alreadyAttemptedConnectionsList[peer] = 0
            shared.alreadyAttemptedConnectionsListLock.release()
            timeNodeLastSeen = shared.knownNodes[
                self.streamNumber][peer]
            sock = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM)
            # This option apparently avoids the TIME_WAIT state so that we
            # can rebind faster
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.settimeout(20)
            if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2:
                logger.debug('Trying an outgoing connection to %s'%peer)

                # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS4a':
                if shared.verbose >= 2:
                    logger.debug('(Using SOCKS4a) Trying an outgoing connection to %s'%peer)

                proxytype = socks.PROXY_TYPE_SOCKS4
                sockshostname = shared.config.get(
                    'bitmessagesettings', 'sockshostname')
                socksport = shared.config.getint(
                    'bitmessagesettings', 'socksport')
                rdns = True  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean('bitmessagesettings', 'socksauthentication'):
                    socksusername = shared.config.get(
                        'bitmessagesettings', 'socksusername')
                    sockspassword = shared.config.get(
                        'bitmessagesettings', 'sockspassword')
                    sock.setproxy(
                        proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
                else:
                    sock.setproxy(
                        proxytype, sockshostname, socksport, rdns)
            elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5':
                if shared.verbose >= 2:
                    logger.debug('(Using SOCKS5) Trying an outgoing connection to %s'%peer)

                proxytype = socks.PROXY_TYPE_SOCKS5
                sockshostname = shared.config.get(
                    'bitmessagesettings', 'sockshostname')
                socksport = shared.config.getint(
                    'bitmessagesettings', 'socksport')
                rdns = True  # Do domain name lookups through the proxy; though this setting doesn't really matter since we won't be doing any domain name lookups anyway.
                if shared.config.getboolean('bitmessagesettings', 'socksauthentication'):
                    socksusername = shared.config.get(
                        'bitmessagesettings', 'socksusername')
                    sockspassword = shared.config.get(
                        'bitmessagesettings', 'sockspassword')
                    sock.setproxy(
                        proxytype, sockshostname, socksport, rdns, socksusername, sockspassword)
                else:
                    sock.setproxy(
                        proxytype, sockshostname, socksport, rdns)

            try:
                sock.connect((peer.host, peer.port))
                rd = receiveDataThread()
                rd.daemon = True  # close the main program even if there are threads left
                someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory.
                rd.setup(sock, peer.host, peer.port, self.streamNumber,
                         someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections)
                rd.start()
                logger.debug('%s connected to %s during an outgoing attempt.'%(self,peer))


                sd = sendDataThread()
                sd.setup(sock, peer.host, peer.port, self.streamNumber,
                         someObjectsOfWhichThisRemoteNodeIsAlreadyAware)
                sd.start()
                sd.sendVersionMessage()

            except socks.GeneralProxyError as err:
                if shared.verbose >= 2:
                    logger.debug('Could NOT connect to %s during outgoing attempt. %s'%(peer,err))

                timeLastSeen = shared.knownNodes[
                    self.streamNumber][peer]
                if (int(time.time()) - timeLastSeen) > 172800 and len(shared.knownNodes[self.streamNumber]) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the shared.knownNodes data-structure.
                    shared.knownNodesLock.acquire()
                    del shared.knownNodes[self.streamNumber][peer]
                    shared.knownNodesLock.release()
                    logger.debug('deleting %s from shared.knownNodes because it is more than 48 hours old and we could not connect to it.'%peer)

            except socks.Socks5AuthError as err:
                shared.UISignalQueue.put((
                    'updateStatusBar', tr.translateText(
                    "MainWindow", "SOCKS5 Authentication problem: %1").arg(str(err))))
            except socks.Socks5Error as err:
                pass
                logger.debug('SOCKS5 error. (It is possible that the server wants authentication).) %s'%str(err))
            except socks.Socks4Error as err:
                logger.debug('Socks4Error: %s'%err)
            except socket.error as err:
                if shared.config.get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS':
                    logger.debug('Bitmessage MIGHT be having trouble connecting to the SOCKS server. %s'%str(err))
                else:
                    if shared.verbose >= 1:
                        logger.debug('Could NOT connect to %s during outgoing attempt. %s'%(peer, err))

                    timeLastSeen = shared.knownNodes[
                        self.streamNumber][peer]
                    if (int(time.time()) - timeLastSeen) > 172800 and len(shared.knownNodes[self.streamNumber]) > 1000:  # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the knownNodes data-structure.
                        shared.knownNodesLock.acquire()
                        del shared.knownNodes[self.streamNumber][peer]
                        shared.knownNodesLock.release()
                        logger.debug('deleting %s from knownNodes because it is more than 48 hours old and we could not connect to it.'%str(peer))

            except Exception as err:
                sys.stderr.write(
                    'An exception has occurred in the outgoingSynSender thread that was not caught by other exception types: ')
                import traceback
                traceback.print_exc()
            time.sleep(0.1)
Exemple #28
0
    def run(self):
        while True:
            queueValue = shared.addressGeneratorQueue.get()
            nonceTrialsPerByte = 0
            payloadLengthExtraBytes = 0
            if queueValue[0] == 'createChan':
                command, addressVersionNumber, streamNumber, label, deterministicPassphrase = queueValue
                eighteenByteRipe = False
                numberOfAddressesToMake = 1
            elif queueValue[0] == 'joinChan':
                command, chanAddress, label, deterministicPassphrase = queueValue
                eighteenByteRipe = False
                addressVersionNumber = decodeAddress(chanAddress)[1]
                streamNumber = decodeAddress(chanAddress)[2]
                numberOfAddressesToMake = 1
            elif len(queueValue) == 7:
                command, addressVersionNumber, streamNumber, label, numberOfAddressesToMake, deterministicPassphrase, eighteenByteRipe = queueValue
            elif len(queueValue) == 9:
                command, addressVersionNumber, streamNumber, label, numberOfAddressesToMake, deterministicPassphrase, eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes = queueValue
            else:
                sys.stderr.write(
                    'Programming error: A structure with the wrong number of values was passed into the addressGeneratorQueue. Here is the queueValue: %s\n' % repr(queueValue))
            if addressVersionNumber < 3 or addressVersionNumber > 3:
                sys.stderr.write(
                    'Program error: For some reason the address generator queue has been given a request to create at least one version %s address which it cannot do.\n' % addressVersionNumber)
            if nonceTrialsPerByte == 0:
                nonceTrialsPerByte = shared.config.getint(
                    'bitmessagesettings', 'defaultnoncetrialsperbyte')
            if nonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte:
                nonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
            if payloadLengthExtraBytes == 0:
                payloadLengthExtraBytes = shared.config.getint(
                    'bitmessagesettings', 'defaultpayloadlengthextrabytes')
            if payloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
                payloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
            if addressVersionNumber == 3:  # currently the only one supported.
                if command == 'createRandomAddress':
                    shared.UISignalQueue.put((
                        'updateStatusBar', tr.translateText("MainWindow", "Generating one new address")))
                    # This next section is a little bit strange. We're going to generate keys over and over until we
                    # find one that starts with either \x00 or \x00\x00. Then when we pack them into a Bitmessage address,
                    # we won't store the \x00 or \x00\x00 bytes thus making the
                    # address shorter.
                    startTime = time.time()
                    numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
                    potentialPrivSigningKey = OpenSSL.rand(32)
                    potentialPubSigningKey = pointMult(potentialPrivSigningKey)
                    while True:
                        numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
                        potentialPrivEncryptionKey = OpenSSL.rand(32)
                        potentialPubEncryptionKey = pointMult(
                            potentialPrivEncryptionKey)
                        # print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex')
                        # print 'potentialPubEncryptionKey',
                        # potentialPubEncryptionKey.encode('hex')
                        ripe = hashlib.new('ripemd160')
                        sha = hashlib.new('sha512')
                        sha.update(
                            potentialPubSigningKey + potentialPubEncryptionKey)
                        ripe.update(sha.digest())
                        # print 'potential ripe.digest',
                        # ripe.digest().encode('hex')
                        if eighteenByteRipe:
                            if ripe.digest()[:2] == '\x00\x00':
                                break
                        else:
                            if ripe.digest()[:1] == '\x00':
                                break
                    print 'Generated address with ripe digest:', ripe.digest().encode('hex')
                    print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'addresses per second before finding one with the correct ripe-prefix.'
                    address = encodeAddress(3, streamNumber, ripe.digest())

                    # An excellent way for us to store our keys is in Wallet Import Format. Let us convert now.
                    # https://en.bitcoin.it/wiki/Wallet_import_format
                    privSigningKey = '\x80' + potentialPrivSigningKey
                    checksum = hashlib.sha256(hashlib.sha256(
                        privSigningKey).digest()).digest()[0:4]
                    privSigningKeyWIF = arithmetic.changebase(
                        privSigningKey + checksum, 256, 58)
                    # print 'privSigningKeyWIF',privSigningKeyWIF

                    privEncryptionKey = '\x80' + potentialPrivEncryptionKey
                    checksum = hashlib.sha256(hashlib.sha256(
                        privEncryptionKey).digest()).digest()[0:4]
                    privEncryptionKeyWIF = arithmetic.changebase(
                        privEncryptionKey + checksum, 256, 58)
                    # print 'privEncryptionKeyWIF',privEncryptionKeyWIF

                    shared.config.add_section(address)
                    shared.config.set(address, 'label', label)
                    shared.config.set(address, 'enabled', 'true')
                    shared.config.set(address, 'decoy', 'false')
                    shared.config.set(address, 'noncetrialsperbyte', str(
                        nonceTrialsPerByte))
                    shared.config.set(address, 'payloadlengthextrabytes', str(
                        payloadLengthExtraBytes))
                    shared.config.set(
                        address, 'privSigningKey', privSigningKeyWIF)
                    shared.config.set(
                        address, 'privEncryptionKey', privEncryptionKeyWIF)
                    with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                        shared.config.write(configfile)

                    # The API and the join and create Chan functionality
                    # both need information back from the address generator.
                    shared.apiAddressGeneratorReturnQueue.put(address)

                    shared.UISignalQueue.put((
                        'updateStatusBar', tr.translateText("MainWindow", "Done generating address. Doing work necessary to broadcast it...")))
                    shared.UISignalQueue.put(('writeNewAddressToTable', (
                        label, address, streamNumber)))
                    shared.reloadMyAddressHashes()
                    shared.workerQueue.put((
                        'sendOutOrStoreMyV3Pubkey', ripe.digest()))

                elif command == 'createDeterministicAddresses' or command == 'getDeterministicAddress' or command == 'createChan' or command == 'joinChan':
                    if len(deterministicPassphrase) == 0:
                        sys.stderr.write(
                            'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.')
                    if command == 'createDeterministicAddresses':
                        statusbar = 'Generating ' + str(
                            numberOfAddressesToMake) + ' new addresses.'
                        shared.UISignalQueue.put((
                            'updateStatusBar', statusbar))
                    signingKeyNonce = 0
                    encryptionKeyNonce = 1
                    listOfNewAddressesToSendOutThroughTheAPI = [
                    ]  # We fill out this list no matter what although we only need it if we end up passing the info to the API.

                    for i in range(numberOfAddressesToMake):
                        # This next section is a little bit strange. We're going to generate keys over and over until we
                        # find one that has a RIPEMD hash that starts with either \x00 or \x00\x00. Then when we pack them
                        # into a Bitmessage address, we won't store the \x00 or
                        # \x00\x00 bytes thus making the address shorter.
                        startTime = time.time()
                        numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
                        while True:
                            numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
                            potentialPrivSigningKey = hashlib.sha512(
                                deterministicPassphrase + encodeVarint(signingKeyNonce)).digest()[:32]
                            potentialPrivEncryptionKey = hashlib.sha512(
                                deterministicPassphrase + encodeVarint(encryptionKeyNonce)).digest()[:32]
                            potentialPubSigningKey = pointMult(
                                potentialPrivSigningKey)
                            potentialPubEncryptionKey = pointMult(
                                potentialPrivEncryptionKey)
                            # print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex')
                            # print 'potentialPubEncryptionKey',
                            # potentialPubEncryptionKey.encode('hex')
                            signingKeyNonce += 2
                            encryptionKeyNonce += 2
                            ripe = hashlib.new('ripemd160')
                            sha = hashlib.new('sha512')
                            sha.update(
                                potentialPubSigningKey + potentialPubEncryptionKey)
                            ripe.update(sha.digest())
                            # print 'potential ripe.digest',
                            # ripe.digest().encode('hex')
                            if eighteenByteRipe:
                                if ripe.digest()[:2] == '\x00\x00':
                                    break
                            else:
                                if ripe.digest()[:1] == '\x00':
                                    break

                        print 'ripe.digest', ripe.digest().encode('hex')
                        print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'keys per second.'
                        address = encodeAddress(3, streamNumber, ripe.digest())

                        saveAddressToDisk = True
                        # If we are joining an existing chan, let us check to make sure it matches the provided Bitmessage address
                        if command == 'joinChan':
                            if address != chanAddress:
                                shared.apiAddressGeneratorReturnQueue.put('chan name does not match address')
                                saveAddressToDisk = False
                        if command == 'getDeterministicAddress':
                            saveAddressToDisk = False

                        if saveAddressToDisk:
                            # An excellent way for us to store our keys is in Wallet Import Format. Let us convert now.
                            # https://en.bitcoin.it/wiki/Wallet_import_format
                            privSigningKey = '\x80' + potentialPrivSigningKey
                            checksum = hashlib.sha256(hashlib.sha256(
                                privSigningKey).digest()).digest()[0:4]
                            privSigningKeyWIF = arithmetic.changebase(
                                privSigningKey + checksum, 256, 58)

                            privEncryptionKey = '\x80' + \
                                potentialPrivEncryptionKey
                            checksum = hashlib.sha256(hashlib.sha256(
                                privEncryptionKey).digest()).digest()[0:4]
                            privEncryptionKeyWIF = arithmetic.changebase(
                                privEncryptionKey + checksum, 256, 58)

                            addressAlreadyExists = False
                            try:
                                shared.config.add_section(address)
                            except:
                                print address, 'already exists. Not adding it again.'
                                addressAlreadyExists = True
                            if not addressAlreadyExists:
                                print 'label:', label
                                shared.config.set(address, 'label', label)
                                shared.config.set(address, 'enabled', 'true')
                                shared.config.set(address, 'decoy', 'false')
                                if command == 'joinChan' or command == 'createChan':
                                    shared.config.set(address, 'chan', 'true')
                                shared.config.set(address, 'noncetrialsperbyte', str(
                                    nonceTrialsPerByte))
                                shared.config.set(address, 'payloadlengthextrabytes', str(
                                    payloadLengthExtraBytes))
                                shared.config.set(
                                    address, 'privSigningKey', privSigningKeyWIF)
                                shared.config.set(
                                    address, 'privEncryptionKey', privEncryptionKeyWIF)
                                with open(shared.appdata + 'keys.dat', 'wb') as configfile:
                                    shared.config.write(configfile)

                                shared.UISignalQueue.put(('writeNewAddressToTable', (
                                    label, address, str(streamNumber))))
                                listOfNewAddressesToSendOutThroughTheAPI.append(
                                    address)
                                shared.myECCryptorObjects[ripe.digest()] = highlevelcrypto.makeCryptor(
                                    potentialPrivEncryptionKey.encode('hex'))
                                shared.myAddressesByHash[
                                    ripe.digest()] = address
                                shared.workerQueue.put((
                                    'sendOutOrStoreMyV3Pubkey', ripe.digest())) # If this is a chan address,
                                        # the worker thread won't send out the pubkey over the network.


                    # Done generating addresses.
                    if command == 'createDeterministicAddresses' or command == 'joinChan' or command == 'createChan':
                        shared.apiAddressGeneratorReturnQueue.put(
                            listOfNewAddressesToSendOutThroughTheAPI)
                        shared.UISignalQueue.put((
                            'updateStatusBar', tr.translateText("MainWindow", "Done generating address")))
                        # shared.reloadMyAddressHashes()
                    elif command == 'getDeterministicAddress':
                        shared.apiAddressGeneratorReturnQueue.put(address)
                    #todo: return things to the API if createChan or joinChan assuming saveAddressToDisk
                else:
                    raise Exception(
                        "Error in the addressGenerator thread. Thread was given a command it could not understand: " + command)