2014-04-19 20:45:37 +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-07-29 08:51:59 +02:00
import sys
#Version check
#Older versions of Python don't support the print function while Python 3 doesn't
#like the print statement, so we use sys.stdout for the version check. After this
#check we can then use the print function in the remainder of this file. Currently
#in order to use logging, a lot of unnecessary code needs to be executed which could
#potentially render this version check useless. So logging won't be used here until
#there is a more efficient way to configure logging
if sys . hexversion > = 0x3000000 :
2014-08-06 21:54:59 +02:00
msg = " PyBitmessage does not support Python 3. Python 2.7.3 or later is required. Your version: %s " % sys . version
2014-07-29 08:51:59 +02:00
#logger.critical(msg)
sys . stdout . write ( msg )
sys . exit ( 0 )
2014-08-06 21:54:59 +02:00
if sys . hexversion < 0x20703F0 :
msg = " You should use Python 2.7.3 or greater (but not Python 3). Your version: %s " % sys . version
2014-07-29 08:51:59 +02:00
#logger.critical(msg)
sys . stdout . write ( msg )
sys . exit ( 0 )
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
2013-05-01 22:06:55 +02:00
2013-12-30 01:53:44 +01:00
from SimpleXMLRPCServer import SimpleXMLRPCServer
2014-01-20 21:25:02 +01:00
from api import MySimpleXMLRPCRequestHandler
from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections
2013-12-30 01:53:44 +01:00
import shared
2014-08-27 09:14:32 +02:00
import helper_startup
helper_startup . loadConfig ( )
2013-12-30 01:53:44 +01:00
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 helper_sql import *
#from class_sqlThread import *
from class_sqlThread import sqlThread
from class_singleCleaner import singleCleaner
#from class_singleWorker import *
from class_objectProcessor import objectProcessor
from class_outgoingSynSender import outgoingSynSender
from class_singleListener import singleListener
from class_singleWorker import singleWorker
#from class_addressGenerator import *
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
2013-06-21 00:55:04 +02:00
2013-12-30 01:53:44 +01:00
from subprocess import call
import time
2014-07-29 08:51:59 +02:00
2014-01-13 01:30:01 +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
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.
2013-05-01 22:06:55 +02:00
class singleAPI ( threading . Thread ) :
2013-06-13 20:00:56 +02:00
2013-05-01 22:06:55 +02:00
def __init__ ( self ) :
threading . Thread . __init__ ( self )
2013-03-19 18:32:37 +01:00
def run ( self ) :
2013-06-13 20:00:56 +02:00
se = SimpleXMLRPCServer ( ( shared . config . get ( ' bitmessagesettings ' , ' apiinterface ' ) , shared . config . getint (
' 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.
2014-01-13 01:50:44 +01:00
thisapp = singleton . singleinstance ( )
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
2013-08-06 13:23:56 +02: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 ( )
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-07-15 17:32:00 +02:00
try :
from PyQt4 import QtCore , QtGui
except Exception as err :
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 ( ' Error message: ' , err )
print ( ' You can also run PyBitmessage with the new curses interface by providing \' -c \' as a commandline argument. ' )
2014-07-15 17:32:00 +02:00
os . _exit ( 0 )
2014-04-19 20:45:37 +02:00
import bitmessageqt
bitmessageqt . run ( )
else :
2014-07-29 08:51:59 +02:00
print ( ' Running with curses ' )
2014-04-19 20:45:37 +02:00
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 ( )
mainprogram . start ( )
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