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
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
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
2017-01-11 17:00:00 +01:00
import state
2017-01-11 17:26:25 +01:00
import protocol
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_outgoingSynSender import outgoingSynSender
from class_singleListener import singleListener
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-01-11 14:27:19 +01:00
from configparser import BMConfigParser
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 ) :
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.
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
2017-01-11 14:27:19 +01:00
if BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' socksproxytype ' ) != ' none ' :
2016-03-22 14:47:18 +01:00
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 :
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-01-11 14:27:19 +01:00
se = StoppableXMLRPCServer ( ( BMConfigParser ( ) . get ( ' bitmessagesettings ' , ' apiinterface ' ) , BMConfigParser ( ) . 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 :
2017-01-11 17:00:00 +01:00
protocol . networkDefaultProofOfWorkNonceTrialsPerByte = int (
protocol . networkDefaultProofOfWorkNonceTrialsPerByte / 100 )
protocol . networkDefaultPayloadLengthExtraBytes = int (
protocol . 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
2014-04-19 20:45:37 +02:00
# get curses flag
if ' -c ' in sys . argv :
2017-02-02 15:48:56 +01:00
state . curses = True
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
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 ( )
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
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
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
2016-06-30 12:30:05 +02:00
while True :
time . sleep ( 20 )
def daemonize ( self ) :
if os . fork ( ) :
exit ( 0 )
2016-06-30 13:07:28 +02:00
shared . thisapp . lock ( ) # relock
2016-06-30 12:30:05 +02:00
os . umask ( 0 )
os . setsid ( )
if os . fork ( ) :
exit ( 0 )
2016-06-30 13:07:28 +02:00
shared . thisapp . lock ( ) # relock
shared . thisapp . lockPid = None # indicate we're the final child
2016-06-30 12:30:05 +02:00
sys . stdout . flush ( )
sys . stderr . flush ( )
si = file ( ' /dev/null ' , ' r ' )
so = file ( ' /dev/null ' , ' a+ ' )
se = file ( ' /dev/null ' , ' a+ ' , 0 )
os . dup2 ( si . fileno ( ) , sys . stdin . fileno ( ) )
os . dup2 ( so . fileno ( ) , sys . stdout . fileno ( ) )
os . dup2 ( se . fileno ( ) , sys . stderr . fileno ( ) )
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
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
2013-08-05 22:29:06 +02:00
if __name__ == " __main__ " :
2013-08-06 13:23:56 +02:00
mainprogram = Main ( )
2017-01-11 14:27:19 +01:00
mainprogram . start ( BMConfigParser ( ) . safeGetBoolean ( ' 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