diff --git a/src/api.py b/src/api.py index a32519e4..0fa97578 100644 --- a/src/api.py +++ b/src/api.py @@ -8,8 +8,13 @@ This is not what you run to run the Bitmessage API. Instead, enable the API """ import base64 +import errno import hashlib import json +import random # nosec +import socket +import subprocess +import threading import time from binascii import hexlify, unhexlify from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer @@ -23,6 +28,7 @@ from bmconfigparser import BMConfigParser import defaults import helper_inbox import helper_sent +import helper_threading import state import queues @@ -60,6 +66,68 @@ class StoppableXMLRPCServer(SimpleXMLRPCServer): self.handle_request() +# This thread, of which there is only one, runs the API. +class singleAPI(threading.Thread, helper_threading.StoppableThread): + def __init__(self): + 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(( + BMConfigParser().get('bitmessagesettings', 'apiinterface'), + BMConfigParser().getint('bitmessagesettings', 'apiport') + )) + s.shutdown(socket.SHUT_RDWR) + s.close() + except: + pass + + def run(self): + 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 = random.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 + se.register_introspection_functions() + + apiNotifyPath = BMConfigParser().safeGet( + 'bitmessagesettings', 'apinotifypath') + + if apiNotifyPath: + logger.info('Trying to call %s', apiNotifyPath) + try: + subprocess.call([apiNotifyPath, "startingUp"]) + except OSError: + logger.warning( + 'Failed to call %s, removing apinotifypath setting', + apiNotifyPath) + BMConfigParser().remove_option( + 'bitmessagesettings', 'apinotifypath') + + se.serve_forever() + + # This is one of several classes that constitute the API # This class was written by Vaibhav Bhatia. # Modified by Jonathan Warren (Atheros). diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index fb3cccb9..d6094f0d 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -20,23 +20,20 @@ sys.path.insert(0, app_dir) import depends depends.check_dependencies() +import ctypes +import getopt # Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully. import signal -# The next 3 are used for the API -from singleinstance import singleinstance -import errno import socket -import ctypes +from datetime import datetime from struct import pack from subprocess import call from time import sleep -from random import randint -import getopt -from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer from helper_startup import ( isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections ) +from singleinstance import singleinstance import defaults import shared @@ -65,7 +62,6 @@ from network.addrthread import AddrThread from network.downloadthread import DownloadThread # Helper Functions -import helper_bootstrap import helper_generic import helper_threading @@ -155,53 +151,6 @@ def _fixSocket(): socket.IPV6_V6ONLY = 27 -# This thread, of which there is only one, runs the API. -class singleAPI(threading.Thread, helper_threading.StoppableThread): - def __init__(self): - 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(( - BMConfigParser().get('bitmessagesettings', 'apiinterface'), - BMConfigParser().getint('bitmessagesettings', 'apiport') - )) - s.shutdown(socket.SHUT_RDWR) - s.close() - except: - pass - - def run(self): - 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 - se.register_introspection_functions() - se.serve_forever() - - # This is a list of current connections (the thread pointers at least) selfInitiatedConnections = {} @@ -338,19 +287,9 @@ class Main: shared.reloadBroadcastSendersForWhichImWatching() # API is also objproc dependent - if BMConfigParser().safeGetBoolean( - 'bitmessagesettings', 'apienabled'): - try: - apiNotifyPath = BMConfigParser().get( - 'bitmessagesettings', 'apinotifypath') - except: - apiNotifyPath = '' - if apiNotifyPath != '': - with shared.printLock: - print('Trying to call', apiNotifyPath) - - call([apiNotifyPath, "startingUp"]) - singleAPIThread = singleAPI() + if BMConfigParser().safeGetBoolean('bitmessagesettings', 'apienabled'): + import api + singleAPIThread = api.singleAPI() # close the main program even if there are threads left singleAPIThread.daemon = True singleAPIThread.start()