2015-08-22 10:48:49 +02:00
#!/usr/bin/env python2.7
2012-11-19 20:45:05 +01:00
# Copyright (c) 2012 Jonathan Warren
# Copyright (c) 2012 The Bitmessage developers
# Distributed under the MIT/X11 software license. See the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2013-06-13 20:00:56 +02:00
# Right now, PyBitmessage only support connecting to stream 1. It doesn't
# yet contain logic to expand into further streams.
2012-11-19 20:45:05 +01:00
2013-06-13 20:00:56 +02:00
# The software version variable is now held in shared.py
2013-02-18 21:22:48 +01:00
2014-08-06 08:40:41 +02:00
import depends
depends . check_dependencies ( )
2014-07-29 08:51:59 +02:00
2013-06-13 20:00:56 +02:00
import signal # Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
# The next 3 are used for the API
2013-05-13 11:29:14 +02:00
import singleton
2013-06-25 22:26:12 +02:00
import os
2014-02-16 17:21:20 +01:00
import socket
import ctypes
from struct import pack
2014-08-06 08:40:41 +02:00
import sys
2015-03-19 23:09:04 +01:00
from subprocess import call
import time
2013-05-01 22:06:55 +02:00
2015-11-24 01:55:17 +01:00
from api import MySimpleXMLRPCRequestHandler , StoppableXMLRPCServer
2014-01-20 21:25:02 +01:00
from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections
2013-12-30 01:53:44 +01:00
import shared
from helper_sql import sqlQuery
import threading
2013-06-20 23:23:03 +02:00
# Classes
2013-12-30 01:53:44 +01:00
from class_sqlThread import sqlThread
from class_singleCleaner import singleCleaner
from class_objectProcessor import objectProcessor
from class_outgoingSynSender import outgoingSynSender
from class_singleListener import singleListener
from class_singleWorker import singleWorker
from class_addressGenerator import addressGenerator
2013-08-25 22:23:28 +02:00
from debug import logger
2013-06-20 23:23:03 +02:00
2013-06-21 00:55:04 +02:00
# Helper Functions
import helper_bootstrap
2013-12-30 01:53:44 +01:00
import helper_generic
2015-11-24 01:55:17 +01:00
from helper_threading import *
2015-11-15 15:08:48 +01:00
2013-05-01 22:06:55 +02:00
def connectToStream ( streamNumber ) :
2013-11-20 07:29:37 +01:00
shared . streamsInWhichIAmParticipating [ streamNumber ] = ' no data '
2013-05-01 22:06:55 +02:00
selfInitiatedConnections [ streamNumber ] = { }
2013-09-07 00:55:12 +02:00
shared . inventorySets [ streamNumber ] = set ( )
queryData = sqlQuery ( ''' SELECT hash FROM inventory WHERE streamnumber=? ''' , streamNumber )
for row in queryData :
shared . inventorySets [ streamNumber ] . add ( row [ 0 ] )
2014-01-20 21:25:02 +01:00
if isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections ( ) :
# Some XP and Vista systems can only have 10 outgoing connections at a time.
2013-05-08 23:11:16 +02:00
maximumNumberOfHalfOpenConnections = 9
else :
2014-01-20 21:25:02 +01:00
maximumNumberOfHalfOpenConnections = 64
2016-03-22 14:47:18 +01:00
try :
# don't overload Tor
if shared . config . get ( ' bitmessagesettings ' , ' socksproxytype ' ) != ' none ' :
maximumNumberOfHalfOpenConnections = 4
except :
pass
2013-05-08 23:11:16 +02:00
for i in range ( maximumNumberOfHalfOpenConnections ) :
2013-05-01 22:06:55 +02:00
a = outgoingSynSender ( )
2013-06-22 01:49:50 +02:00
a . setup ( streamNumber , selfInitiatedConnections )
2013-05-01 22:06:55 +02:00
a . start ( )
2014-02-16 17:21:20 +01:00
def _fixWinsock ( ) :
if not ( ' win32 ' in sys . platform ) and not ( ' win64 ' in sys . platform ) :
return
# Python 2 on Windows doesn't define a wrapper for
# socket.inet_ntop but we can make one ourselves using ctypes
if not hasattr ( socket , ' inet_ntop ' ) :
addressToString = ctypes . windll . ws2_32 . WSAAddressToStringA
def inet_ntop ( family , host ) :
if family == socket . AF_INET :
if len ( host ) != 4 :
raise ValueError ( " invalid IPv4 host " )
host = pack ( " hH4s8s " , socket . AF_INET , 0 , host , " \0 " * 8 )
elif family == socket . AF_INET6 :
if len ( host ) != 16 :
raise ValueError ( " invalid IPv6 host " )
host = pack ( " hHL16sL " , socket . AF_INET6 , 0 , 0 , host , 0 )
else :
raise ValueError ( " invalid address family " )
buf = " \0 " * 64
lengthBuf = pack ( " I " , len ( buf ) )
addressToString ( host , len ( host ) , None , buf , lengthBuf )
return buf [ 0 : buf . index ( " \0 " ) ]
socket . inet_ntop = inet_ntop
# Same for inet_pton
if not hasattr ( socket , ' inet_pton ' ) :
stringToAddress = ctypes . windll . ws2_32 . WSAStringToAddressA
def inet_pton ( family , host ) :
buf = " \0 " * 28
lengthBuf = pack ( " I " , len ( buf ) )
if stringToAddress ( str ( host ) ,
int ( family ) ,
None ,
buf ,
lengthBuf ) != 0 :
raise socket . error ( " illegal IP address passed to inet_pton " )
if family == socket . AF_INET :
return buf [ 4 : 8 ]
elif family == socket . AF_INET6 :
return buf [ 8 : 24 ]
else :
raise ValueError ( " invalid address family " )
socket . inet_pton = inet_pton
# These sockopts are needed on for IPv6 support
if not hasattr ( socket , ' IPPROTO_IPV6 ' ) :
socket . IPPROTO_IPV6 = 41
if not hasattr ( socket , ' IPV6_V6ONLY ' ) :
socket . IPV6_V6ONLY = 27
2013-04-26 19:20:30 +02:00
2013-06-13 20:00:56 +02:00
# This thread, of which there is only one, runs the API.
2015-11-24 01:55:17 +01:00
class singleAPI ( threading . Thread , StoppableThread ) :
2013-05-01 22:06:55 +02:00
def __init__ ( self ) :
2015-11-24 01:55:17 +01:00
threading . Thread . __init__ ( self , name = " singleAPI " )
self . initStop ( )
def stopThread ( self ) :
super ( singleAPI , self ) . stopThread ( )
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
try :
s . connect ( ( shared . config . get ( ' bitmessagesettings ' , ' apiinterface ' ) , shared . config . getint (
' bitmessagesettings ' , ' apiport ' ) ) )
s . shutdown ( socket . SHUT_RDWR )
s . close ( )
except :
pass
2013-03-19 18:32:37 +01:00
def run ( self ) :
2015-11-24 01:55:17 +01:00
se = StoppableXMLRPCServer ( ( shared . config . get ( ' bitmessagesettings ' , ' apiinterface ' ) , shared . config . getint (
2013-06-13 20:00:56 +02:00
' bitmessagesettings ' , ' apiport ' ) ) , MySimpleXMLRPCRequestHandler , True , True )
2013-03-19 18:32:37 +01:00
se . register_introspection_functions ( )
se . serve_forever ( )
2013-08-06 19:19:26 +02:00
# This is a list of current connections (the thread pointers at least)
2013-06-24 21:51:01 +02:00
selfInitiatedConnections = { }
if shared . useVeryEasyProofOfWorkForTesting :
2013-06-13 20:00:56 +02:00
shared . networkDefaultProofOfWorkNonceTrialsPerByte = int (
2014-08-27 09:14:32 +02:00
shared . networkDefaultProofOfWorkNonceTrialsPerByte / 100 )
2013-06-13 20:00:56 +02:00
shared . networkDefaultPayloadLengthExtraBytes = int (
2014-08-27 09:14:32 +02:00
shared . networkDefaultPayloadLengthExtraBytes / 100 )
2013-01-18 23:38:09 +01:00
2013-08-06 13:23:56 +02:00
class Main :
2013-08-07 23:02:53 +02:00
def start ( self , daemon = False ) :
2014-02-16 17:21:20 +01:00
_fixWinsock ( )
2013-09-05 02:14:25 +02:00
shared . daemon = daemon
2013-08-06 13:23:56 +02:00
# is the application already running? If yes then exit.
2016-01-26 13:01:28 +01:00
shared . thisapp = singleton . singleinstance ( " " , daemon )
2013-08-06 13:23:56 +02:00
2014-04-19 20:45:37 +02:00
# get curses flag
curses = False
if ' -c ' in sys . argv :
curses = True
2013-08-06 13:23:56 +02:00
signal . signal ( signal . SIGINT , helper_generic . signal_handler )
# signal.signal(signal.SIGINT, signal.SIG_DFL)
helper_bootstrap . knownNodes ( )
# Start the address generation thread
addressGeneratorThread = addressGenerator ( )
addressGeneratorThread . daemon = True # close the main program even if there are threads left
addressGeneratorThread . start ( )
# Start the thread that calculates POWs
singleWorkerThread = singleWorker ( )
singleWorkerThread . daemon = True # close the main program even if there are threads left
singleWorkerThread . start ( )
# Start the SQL thread
sqlLookup = sqlThread ( )
sqlLookup . daemon = False # DON'T close the main program even if there are threads left. The closeEvent should command this thread to exit gracefully.
sqlLookup . start ( )
2013-12-02 07:35:34 +01:00
# Start the thread that calculates POWs
objectProcessorThread = objectProcessor ( )
objectProcessorThread . daemon = False # DON'T close the main program even the thread remains. This thread checks the shutdown variable after processing each object.
objectProcessorThread . start ( )
2013-08-06 13:23:56 +02:00
# Start the cleanerThread
singleCleanerThread = singleCleaner ( )
singleCleanerThread . daemon = True # close the main program even if there are threads left
singleCleanerThread . start ( )
shared . reloadMyAddressHashes ( )
shared . reloadBroadcastSendersForWhichImWatching ( )
if shared . safeConfigGetBoolean ( ' bitmessagesettings ' , ' apienabled ' ) :
try :
apiNotifyPath = shared . config . get (
' bitmessagesettings ' , ' apinotifypath ' )
except :
apiNotifyPath = ' '
if apiNotifyPath != ' ' :
with shared . printLock :
2014-07-29 08:51:59 +02:00
print ( ' Trying to call ' , apiNotifyPath )
2013-05-02 17:53:54 +02:00
2013-08-06 13:23:56 +02:00
call ( [ apiNotifyPath , " startingUp " ] )
singleAPIThread = singleAPI ( )
singleAPIThread . daemon = True # close the main program even if there are threads left
singleAPIThread . start ( )
2013-06-29 19:29:35 +02:00
2015-03-23 22:35:56 +01:00
connectToStream ( 1 )
2013-05-01 22:06:55 +02:00
2013-08-06 13:23:56 +02:00
singleListenerThread = singleListener ( )
singleListenerThread . setup ( selfInitiatedConnections )
singleListenerThread . daemon = True # close the main program even if there are threads left
singleListenerThread . start ( )
2015-11-21 11:59:44 +01:00
if shared . safeConfigGetBoolean ( ' bitmessagesettings ' , ' upnp ' ) :
import upnp
upnpThread = upnp . uPnPThread ( )
upnpThread . start ( )
2013-05-01 22:06:55 +02:00
2013-08-07 23:02:53 +02:00
if daemon == False and shared . safeConfigGetBoolean ( ' bitmessagesettings ' , ' daemon ' ) == False :
2014-04-19 20:45:37 +02:00
if curses == False :
2014-08-06 08:40:41 +02:00
if not depends . check_pyqt ( ) :
2014-07-29 08:51:59 +02:00
print ( ' PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \' PyQt Download \' . If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon ' )
print ( ' You can also run PyBitmessage with the new curses interface by providing \' -c \' as a commandline argument. ' )
2014-08-06 08:40:41 +02:00
sys . exit ( )
2014-07-15 17:32:00 +02:00
2014-04-19 20:45:37 +02:00
import bitmessageqt
bitmessageqt . run ( )
else :
2014-08-06 08:40:41 +02:00
if depends . check_curses ( ) :
print ( ' Running with curses ' )
import bitmessagecurses
bitmessagecurses . runwrapper ( )
2013-08-06 13:23:56 +02:00
else :
shared . config . remove_option ( ' bitmessagesettings ' , ' dontconnect ' )
2013-05-02 22:55:13 +02:00
2013-08-07 23:02:53 +02:00
if daemon :
2013-08-06 13:23:56 +02:00
with shared . printLock :
2014-07-29 08:51:59 +02:00
print ( ' Running as a daemon. The main program should exit this thread. ' )
2013-08-06 13:23:56 +02:00
else :
with shared . printLock :
2014-07-29 08:51:59 +02:00
print ( ' Running as a daemon. You can use Ctrl+C to exit. ' )
2013-08-06 13:23:56 +02:00
while True :
time . sleep ( 20 )
2013-09-28 14:09:15 +02:00
2013-08-06 13:23:56 +02:00
def stop ( self ) :
2013-06-29 19:29:35 +02:00
with shared . printLock :
2014-07-29 08:51:59 +02:00
print ( ' Stopping Bitmessage Deamon. ' )
2013-08-06 13:23:56 +02:00
shared . doCleanShutdown ( )
2013-09-28 14:09:15 +02:00
2013-12-30 01:53:44 +01:00
#TODO: nice function but no one is using this
2013-08-06 13:23:56 +02:00
def getApiAddress ( self ) :
if not shared . safeConfigGetBoolean ( ' bitmessagesettings ' , ' apienabled ' ) :
return None
address = shared . config . get ( ' bitmessagesettings ' , ' apiinterface ' )
port = shared . config . getint ( ' bitmessagesettings ' , ' apiport ' )
return { ' address ' : address , ' port ' : port }
2013-09-28 14:09:15 +02:00
2013-08-05 22:29:06 +02:00
if __name__ == " __main__ " :
2013-08-06 13:23:56 +02:00
mainprogram = Main ( )
2016-01-17 14:25:46 +01:00
mainprogram . start ( shared . safeConfigGetBoolean ( ' bitmessagesettings ' , ' daemon ' ) )
2013-02-18 21:22:48 +01:00
2013-09-28 14:09:15 +02:00
2013-06-17 22:42:30 +02:00
# So far, the creation of and management of the Bitmessage protocol and this
# client is a one-man operation. Bitcoin tips are quite appreciated.
2012-11-29 11:39:39 +01:00
# 1H5XaDA6fYENLbknwZyjiYXYPQaFjjLX2u