2016-07-16 17:42:19 +02:00
#!/usr/bin/python2.7
2016-05-01 08:34:04 +02:00
# Copyright (c) 2012-2016 Jonathan Warren
# Copyright (c) 2012-2016 The Bitmessage developers
2012-11-19 20:45:05 +01:00
# 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
2017-02-28 14:51:49 +01:00
import os
import sys
app_dir = os . path . dirname ( os . path . abspath ( __file__ ) )
os . chdir ( app_dir )
sys . path . insert ( 0 , app_dir )
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
2017-01-10 21:15:35 +01:00
from singleinstance import singleinstance
2017-05-27 19:00:19 +02:00
import errno
2014-02-16 17:21:20 +01:00
import socket
import ctypes
from struct import pack
2015-03-19 23:09:04 +01:00
from subprocess import call
2017-07-30 09:36:20 +02:00
from time import sleep
2017-08-09 17:29:23 +02:00
from random import randint
2017-09-23 23:42:15 +02:00
import getopt
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
2017-02-08 20:37:42 +01:00
import defaults
2013-12-30 01:53:44 +01:00
import shared
2017-02-08 20:48:22 +01:00
import knownnodes
2017-01-11 17:00:00 +01:00
import state
2017-02-08 13:41:56 +01:00
import shutdown
2013-12-30 01:53:44 +01:00
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_singleWorker import singleWorker
from class_addressGenerator import addressGenerator
2016-06-30 12:30:05 +02:00
from class_smtpDeliver import smtpDeliver
2016-07-19 13:57:54 +02:00
from class_smtpServer import smtpServer
2017-02-22 09:34:54 +01:00
from bmconfigparser import BMConfigParser
2013-06-20 23:23:03 +02:00
2017-05-27 19:09:21 +02:00
from inventory import Inventory
2017-05-24 16:51:49 +02:00
from network . connectionpool import BMConnectionPool
2017-10-20 01:21:49 +02:00
from network . dandelion import Dandelion
2017-05-24 16:51:49 +02:00
from network . networkthread import BMNetworkThread
2017-05-25 23:04:33 +02:00
from network . receivequeuethread import ReceiveQueueThread
2017-05-27 19:09:21 +02:00
from network . announcethread import AnnounceThread
2017-05-29 00:24:07 +02:00
from network . invthread import InvThread
2017-07-05 08:57:44 +02:00
from network . addrthread import AddrThread
2017-06-21 12:16:33 +02:00
from network . downloadthread import DownloadThread
2017-05-24 16:51:49 +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
2017-07-06 19:35:40 +02:00
import helper_threading
2015-11-15 15:08:48 +01:00
2017-02-28 14:51:49 +01:00
2013-05-01 22:06:55 +02:00
def connectToStream ( streamNumber ) :
2017-02-06 17:47:05 +01:00
state . streamsInWhichIAmParticipating . append ( streamNumber )
2013-05-01 22:06:55 +02:00
selfInitiatedConnections [ streamNumber ] = { }
2013-09-07 00:55:12 +02:00
2014-01-20 21:25:02 +01:00
if isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections ( ) :
# Some XP and Vista systems can only have 10 outgoing connections at a time.
2017-05-25 23:04:33 +02:00
state . maximumNumberOfHalfOpenConnections = 9
2013-05-08 23:11:16 +02:00
else :
2017-05-25 23:04:33 +02:00
state . maximumNumberOfHalfOpenConnections = 64
2016-03-22 14:47:18 +01:00
try :
# don't overload Tor
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' socksproxytype ' ) != ' none ' :
2017-05-25 23:04:33 +02:00
state . maximumNumberOfHalfOpenConnections = 4
2016-03-22 14:47:18 +01:00
except :
pass
2017-02-08 20:48:22 +01:00
with knownnodes . knownNodesLock :
if streamNumber not in knownnodes . knownNodes :
knownnodes . knownNodes [ streamNumber ] = { }
2017-02-08 20:52:18 +01:00
if streamNumber * 2 not in knownnodes . knownNodes :
knownnodes . knownNodes [ streamNumber * 2 ] = { }
if streamNumber * 2 + 1 not in knownnodes . knownNodes :
knownnodes . knownNodes [ streamNumber * 2 + 1 ] = { }
2017-02-08 20:48:22 +01:00
2017-08-09 17:36:52 +02:00
BMConnectionPool ( ) . connectToStream ( streamNumber )
2013-05-01 22:06:55 +02:00
2017-05-27 19:00:19 +02:00
def _fixSocket ( ) :
if sys . platform . startswith ( ' linux ' ) :
socket . SO_BINDTODEVICE = 25
if not sys . platform . startswith ( ' win ' ) :
2014-02-16 17:21:20 +01:00
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.
2017-07-06 19:35:40 +02:00
class singleAPI ( threading . Thread , helper_threading . 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 :
2017-01-11 14:27:19 +01:00
s . connect ( ( BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' apiinterface ' ) , BMConfigParser ( ) . getint (
2015-11-24 01:55:17 +01:00
' bitmessagesettings ' , ' apiport ' ) ) )
s . shutdown ( socket . SHUT_RDWR )
s . close ( )
except :
pass
2013-03-19 18:32:37 +01:00
def run ( self ) :
2017-08-09 17:29:23 +02:00
port = BMConfigParser ( ) . getint ( ' bitmessagesettings ' , ' apiport ' )
try :
from errno import WSAEADDRINUSE
except ( ImportError , AttributeError ) :
errno . WSAEADDRINUSE = errno . EADDRINUSE
for attempt in range ( 50 ) :
try :
if attempt > 0 :
port = randint ( 32767 , 65535 )
se = StoppableXMLRPCServer ( ( BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' apiinterface ' ) , port ) ,
MySimpleXMLRPCRequestHandler , True , True )
except socket . error as e :
if e . errno in ( errno . EADDRINUSE , errno . WSAEADDRINUSE ) :
continue
else :
if attempt > 0 :
BMConfigParser ( ) . set ( " bitmessagesettings " , " apiport " , str ( port ) )
BMConfigParser ( ) . save ( )
break
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 :
2017-02-08 20:37:42 +01:00
defaults . networkDefaultProofOfWorkNonceTrialsPerByte = int (
defaults . networkDefaultProofOfWorkNonceTrialsPerByte / 100 )
defaults . networkDefaultPayloadLengthExtraBytes = int (
defaults . networkDefaultPayloadLengthExtraBytes / 100 )
2013-01-18 23:38:09 +01:00
2013-08-06 13:23:56 +02:00
class Main :
2017-09-26 16:36:02 +02:00
def start ( self ) :
2017-05-27 19:00:19 +02:00
_fixSocket ( )
2014-02-16 17:21:20 +01:00
2017-09-26 16:36:02 +02:00
daemon = BMConfigParser ( ) . safeGetBoolean ( ' bitmessagesettings ' , ' daemon ' )
2017-09-23 23:42:15 +02:00
try :
opts , args = getopt . getopt ( sys . argv [ 1 : ] , " hcd " ,
[ " help " , " curses " , " daemon " ] )
except getopt . GetoptError :
self . usage ( )
sys . exit ( 2 )
for opt , arg in opts :
if opt in ( " -h " , " --help " ) :
self . usage ( )
sys . exit ( )
elif opt in ( " -d " , " --daemon " ) :
daemon = True
elif opt in ( " -c " , " --curses " ) :
state . curses = True
2013-08-06 13:23:56 +02:00
2017-09-23 23:42:15 +02:00
shared . daemon = daemon
2014-04-19 20:45:37 +02:00
2016-06-30 12:30:05 +02:00
# is the application already running? If yes then exit.
2017-01-10 21:15:35 +01:00
shared . thisapp = singleinstance ( " " , daemon )
2016-06-30 12:30:05 +02:00
if daemon :
with shared . printLock :
print ( ' Running as a daemon. Send TERM signal to end. ' )
self . daemonize ( )
self . setSignalHandler ( )
2013-08-06 13:23:56 +02:00
2017-09-21 17:51:34 +02:00
helper_threading . set_thread_name ( " PyBitmessage " )
2017-07-06 19:35:40 +02:00
2013-08-06 13:23:56 +02:00
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 ( )
2017-05-27 19:09:21 +02:00
Inventory ( ) # init
2017-10-20 01:21:49 +02:00
Dandelion ( ) # init, needs to be early because other thread may access it early
2017-05-27 19:09:21 +02:00
2016-06-30 12:30:05 +02:00
# SMTP delivery thread
2017-01-11 14:27:19 +01:00
if daemon and BMConfigParser ( ) . safeGet ( " bitmessagesettings " , " smtpdeliver " , ' ' ) != ' ' :
2016-06-30 12:30:05 +02:00
smtpDeliveryThread = smtpDeliver ( )
smtpDeliveryThread . start ( )
2016-07-19 13:57:54 +02:00
# SMTP daemon thread
2017-01-11 14:27:19 +01:00
if daemon and BMConfigParser ( ) . safeGetBoolean ( " bitmessagesettings " , " smtpd " ) :
2016-07-19 13:57:54 +02:00
smtpServerThread = smtpServer ( )
smtpServerThread . 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 ( )
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . safeGetBoolean ( ' bitmessagesettings ' , ' apienabled ' ) :
2013-08-06 13:23:56 +02:00
try :
2017-01-11 14:27:19 +01:00
apiNotifyPath = BMConfigParser ( ) . get (
2013-08-06 13:23:56 +02:00
' 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
2017-08-09 17:36:52 +02:00
BMConnectionPool ( )
asyncoreThread = BMNetworkThread ( )
asyncoreThread . daemon = True
asyncoreThread . start ( )
for i in range ( BMConfigParser ( ) . getint ( " threads " , " receive " ) ) :
receiveQueueThread = ReceiveQueueThread ( i )
receiveQueueThread . daemon = True
receiveQueueThread . start ( )
announceThread = AnnounceThread ( )
announceThread . daemon = True
announceThread . start ( )
state . invThread = InvThread ( )
state . invThread . daemon = True
state . invThread . start ( )
state . addrThread = AddrThread ( )
state . addrThread . daemon = True
state . addrThread . start ( )
state . downloadThread = DownloadThread ( )
state . downloadThread . daemon = True
state . downloadThread . start ( )
2017-05-24 16:51:49 +02:00
2015-03-23 22:35:56 +01:00
connectToStream ( 1 )
2013-05-01 22:06:55 +02:00
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . safeGetBoolean ( ' bitmessagesettings ' , ' upnp ' ) :
2015-11-21 11:59:44 +01:00
import upnp
upnpThread = upnp . uPnPThread ( )
upnpThread . start ( )
2013-05-01 22:06:55 +02:00
2017-01-11 14:27:19 +01:00
if daemon == False and BMConfigParser ( ) . safeGetBoolean ( ' bitmessagesettings ' , ' daemon ' ) == False :
2017-02-02 15:48:56 +01:00
if state . 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 :
2016-06-30 12:30:05 +02:00
if True :
# if depends.check_curses():
2014-08-06 08:40:41 +02:00
print ( ' Running with curses ' )
import bitmessagecurses
bitmessagecurses . runwrapper ( )
2013-08-06 13:23:56 +02:00
else :
2017-01-11 14:27:19 +01:00
BMConfigParser ( ) . remove_option ( ' bitmessagesettings ' , ' dontconnect ' )
2013-05-02 22:55:13 +02:00
2017-07-30 09:36:20 +02:00
if daemon :
while state . shutdown == 0 :
sleep ( 1 )
2016-06-30 12:30:05 +02:00
def daemonize ( self ) :
2017-07-28 08:54:34 +02:00
try :
if os . fork ( ) :
2017-08-15 12:22:24 +02:00
os . _exit ( 0 )
2017-07-28 08:54:34 +02:00
except AttributeError :
# fork not implemented
pass
else :
shared . thisapp . lock ( ) # relock
2016-06-30 12:30:05 +02:00
os . umask ( 0 )
2017-07-28 09:19:53 +02:00
try :
os . setsid ( )
except AttributeError :
# setsid not implemented
pass
2017-07-28 08:54:34 +02:00
try :
if os . fork ( ) :
2017-08-15 12:22:24 +02:00
os . _exit ( 0 )
2017-07-28 08:54:34 +02:00
except AttributeError :
# fork not implemented
pass
else :
shared . thisapp . lock ( ) # relock
2016-06-30 13:07:28 +02:00
shared . thisapp . lockPid = None # indicate we're the final child
2016-06-30 12:30:05 +02:00
sys . stdout . flush ( )
sys . stderr . flush ( )
2017-09-23 18:25:41 +02:00
if not sys . platform . startswith ( ' win ' ) :
si = file ( os . devnull , ' r ' )
so = file ( os . devnull , ' a+ ' )
se = file ( os . devnull , ' a+ ' , 0 )
os . dup2 ( si . fileno ( ) , sys . stdin . fileno ( ) )
os . dup2 ( so . fileno ( ) , sys . stdout . fileno ( ) )
os . dup2 ( se . fileno ( ) , sys . stderr . fileno ( ) )
2016-06-30 12:30:05 +02:00
def setSignalHandler ( self ) :
signal . signal ( signal . SIGINT , helper_generic . signal_handler )
signal . signal ( signal . SIGTERM , helper_generic . signal_handler )
# signal.signal(signal.SIGINT, signal.SIG_DFL)
2013-09-28 14:09:15 +02:00
2017-09-23 23:42:15 +02:00
def usage ( self ) :
print ' Usage: ' + sys . argv [ 0 ] + ' [OPTIONS] '
print '''
Options :
- h , - - help show this help message and exit
- c , - - curses use curses ( text mode ) interface
- d , - - daemon run in daemon ( background ) mode
All parameters are optional .
'''
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. ' )
2017-02-08 13:41:56 +01:00
shutdown . 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 ) :
2017-01-11 14:27:19 +01:00
if not BMConfigParser ( ) . safeGetBoolean ( ' bitmessagesettings ' , ' apienabled ' ) :
2013-08-06 13:23:56 +02:00
return None
2017-01-11 14:27:19 +01:00
address = BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' apiinterface ' )
port = BMConfigParser ( ) . getint ( ' bitmessagesettings ' , ' apiport ' )
2013-08-06 13:23:56 +02:00
return { ' address ' : address , ' port ' : port }
2013-09-28 14:09:15 +02:00
2017-02-28 14:51:49 +01:00
def main ( ) :
2013-08-06 13:23:56 +02:00
mainprogram = Main ( )
2017-09-26 16:36:02 +02:00
mainprogram . start ( )
2017-02-28 14:51:49 +01:00
if __name__ == " __main__ " :
main ( )
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