From 726986c1ebdda9e87e1534ce31be1199672b6375 Mon Sep 17 00:00:00 2001
From: Dmitri Bogomolov <4glitch@gmail.com>
Date: Fri, 26 Oct 2018 00:19:07 +0300
Subject: [PATCH] Implemented JSON-RPC apivariant

---
 setup.py   |  1 +
 src/api.py | 41 +++++++++++++++++++++++++++--------------
 2 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/setup.py b/setup.py
index a6f7844c..14a023be 100644
--- a/setup.py
+++ b/setup.py
@@ -11,6 +11,7 @@ from src.version import softwareVersion
 
 EXTRAS_REQUIRE = {
     'gir': ['pygobject'],
+    'json': ['jsonrpclib'],
     'notify2': ['notify2'],
     'opencl': ['pyopencl', 'numpy'],
     'prctl': ['python_prctl'],  # Named threads
diff --git a/src/api.py b/src/api.py
index e457a8fc..5f2c9ca6 100644
--- a/src/api.py
+++ b/src/api.py
@@ -54,6 +54,7 @@ except ImportError:
 else:
     monkey_patch()
 
+
 str_chan = '[chan]'
 str_broadcast_subscribers = '[Broadcast subscribers]'
 
@@ -64,16 +65,6 @@ class APIError(xmlrpclib.Fault):
         return "API Error %04i: %s" % (self.faultCode, self.faultString)
 
 
-class StoppableXMLRPCServer(SimpleXMLRPCServer):
-    """A SimpleXMLRPCServer that honours state.shutdown"""
-    allow_reuse_address = True
-
-    def serve_forever(self, poll_interval=None):
-        """Start the SimpleXMLRPCServer"""
-        while state.shutdown == 0:
-            self.handle_request()
-
-
 # This thread, of which there is only one, runs the API.
 class singleAPI(StoppableThread):
     """API thread"""
@@ -99,17 +90,38 @@ class singleAPI(StoppableThread):
             getattr(errno, 'WSAEADDRINUSE')
         except AttributeError:
             errno.WSAEADDRINUSE = errno.EADDRINUSE
+
+        RPCServerBase = SimpleXMLRPCServer
+        if BMConfigParser().safeGet(
+                'bitmessagesettings', 'apivariant') == 'json':
+            try:
+                from jsonrpclib.SimpleJSONRPCServer import (
+                    SimpleJSONRPCServer as RPCServerBase)
+            except ImportError:
+                logger.warning(
+                    'jsonrpclib not available, failing back to XML-RPC')
+
+        # Nested class. FIXME not found a better solution.
+        class StoppableRPCServer(RPCServerBase):
+            """A SimpleXMLRPCServer that honours state.shutdown"""
+            allow_reuse_address = True
+
+            def serve_forever(self, poll_interval=None):
+                """Start the RPCServer"""
+                while state.shutdown == 0:
+                    self.handle_request()
+
         for attempt in range(50):
             try:
                 if attempt > 0:
                     logger.warning(
                         'Failed to start API listener on port %s', port)
                     port = random.randint(32767, 65535)
-                se = StoppableXMLRPCServer(
+                se = StoppableRPCServer(
                     (BMConfigParser().get(
                         'bitmessagesettings', 'apiinterface'),
                      port),
-                    BMXMLRPCRequestHandler, True, True)
+                    BMXMLRPCRequestHandler, True, encoding='UTF-8')
             except socket.error as e:
                 if e.errno in (errno.EADDRINUSE, errno.WSAEADDRINUSE):
                     continue
@@ -120,7 +132,8 @@ class singleAPI(StoppableThread):
                         'bitmessagesettings', 'apiport', str(port))
                     BMConfigParser().save()
                 break
-        se.register_instance(BMXMLRPCDispatcher())
+
+        se.register_instance(BMRPCDispatcher())
         se.register_introspection_functions()
 
         apiNotifyPath = BMConfigParser().safeGet(
@@ -288,7 +301,7 @@ class BMXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
         return False
 
 
-class BMXMLRPCDispatcher(object):
+class BMRPCDispatcher(object):
     """This class is used to dispatch API commands"""
     __metaclass__ = CommandHandler