From 5b68aad1846a9c23347e4a439eafe1ae84c90b0e Mon Sep 17 00:00:00 2001
From: A_D <aunderscored@gmail.com>
Date: Fri, 24 Sep 2021 16:39:30 +0200
Subject: [PATCH] Remove protocol.protocolhandler singleton auto-init

In general, doing things like this on import is bad, but this was
changed specifically to remove a bug that causes
--force-localserver-auth to do nothing.

That is caused because while we were careful not to import protocol
until after we were done doing arg things, we did not check to make sure
something else didn't. Companion imports protocol, which thus always
instantiates WindowsProtocolHandler before we can modify some config
state to indicate that we want LinuxProtocolHandler.
---
 EDMarketConnector.py |  9 ++++++---
 companion.py         |  9 +++++----
 protocol.py          | 47 ++++++++++++++++++++++++--------------------
 3 files changed, 37 insertions(+), 28 deletions(-)

diff --git a/EDMarketConnector.py b/EDMarketConnector.py
index 094a6015..ec7db931 100755
--- a/EDMarketConnector.py
+++ b/EDMarketConnector.py
@@ -377,7 +377,7 @@ from edmc_data import ship_name_map
 from hotkey import hotkeymgr
 from l10n import Translations
 from monitor import monitor
-from protocol import protocolhandler
+import protocol
 from theme import theme
 from ttkHyperlinkLabel import HyperlinkLabel
 
@@ -689,7 +689,7 @@ class AppWindow(object):
         self.w.bind_all('<<Quit>>', self.onexit)  # Updater
 
         # Start a protocol handler to handle cAPI registration. Requires main loop to be running.
-        self.w.after_idle(lambda: protocolhandler.start(self.w))
+        self.w.after_idle(lambda: protocol.protocolhandler.start(self.w))
 
         # Load updater after UI creation (for WinSparkle)
         import update
@@ -1698,7 +1698,7 @@ class AppWindow(object):
 
         # Frontier auth/CAPI handling
         logger.info('Closing protocol handler...')
-        protocolhandler.close()
+        protocol.protocolhandler.close()
 
         logger.info('Closing Frontier CAPI sessions...')
         companion.session.close()
@@ -1900,6 +1900,9 @@ sys.path: {sys.path}'''
         logger.info("Dropping all fdev tokens as --forget-frontier-auth was passed")
         companion.Auth.invalidate(None)
 
+    # Create protocol handler
+    protocol.protocolhandler = protocol.get_handler_impl()()
+
     # TODO: unittests in place of these
     # logger.debug('Test from __main__')
     # test_logging()
diff --git a/companion.py b/companion.py
index e61b39a7..16839925 100644
--- a/companion.py
+++ b/companion.py
@@ -33,7 +33,8 @@ from config import appname, appversion, config
 from edmc_data import companion_category_map as category_map
 from EDMCLogging import get_main_logger
 from monitor import monitor
-from protocol import protocolhandler
+# from protocol import protocolhandler
+import protocol
 
 logger = get_main_logger()
 
@@ -375,7 +376,7 @@ class Auth(object):
             f'&code_challenge={challenge}'
             f'&code_challenge_method=S256'
             f'&state={self.state}'
-            f'&redirect_uri={protocolhandler.redirect}'
+            f'&redirect_uri={protocol.protocolhandler.redirect}'
         )
 
         return None
@@ -412,7 +413,7 @@ class Auth(object):
                 'client_id': self.CLIENT_ID,
                 'code_verifier': self.verifier,
                 'code': data['code'][0],
-                'redirect_uri': protocolhandler.redirect,
+                'redirect_uri': protocol.protocolhandler.redirect,
             }
 
             # import http.client as http_client
@@ -698,7 +699,7 @@ class Session(object):
 
         try:
             logger.debug('Trying authorize with payload from handler')
-            self.start_frontier_auth(self.auth.authorize(protocolhandler.lastpayload))  # type: ignore
+            self.start_frontier_auth(self.auth.authorize(protocol.protocolhandler.lastpayload))  # type: ignore
             self.auth = None
 
         except Exception:
diff --git a/protocol.py b/protocol.py
index ce5ea042..6db5d5be 100644
--- a/protocol.py
+++ b/protocol.py
@@ -1,12 +1,13 @@
 """protocol handler for cAPI authorisation."""
 
+# spell-checker: words ntdll GURL alloc wfile instantiatable pyright
 import os
 import sys
 import threading
 import urllib.error
 import urllib.parse
 import urllib.request
-from typing import TYPE_CHECKING, Optional
+from typing import TYPE_CHECKING, Optional, Type
 
 from config import config
 from constants import appname, protocolhandler_redirect
@@ -115,10 +116,10 @@ if sys.platform == 'darwin' and getattr(sys, 'frozen', False):  # noqa: C901 # i
 
 elif (config.auth_force_edmc_protocol
       or (
-              sys.platform == 'win32'
-              and getattr(sys, 'frozen', False)
-              and not is_wine
-              and not config.auth_force_localserver
+          sys.platform == 'win32'
+          and getattr(sys, 'frozen', False)
+          and not is_wine
+          and not config.auth_force_localserver
       )):
     # spell-checker: words HBRUSH HICON WPARAM wstring WNDCLASS HMENU HGLOBAL
     from ctypes import windll  # type: ignore
@@ -415,20 +416,24 @@ else:  # Linux / Run from source
             pass
 
 
-# singleton
+def get_handler_impl() -> Type[GenericProtocolHandler]:
+    """
+    Get the appropriate GenericProtocolHandler for the current system and config.
+
+    :return: An instantiatable GenericProtocolHandler
+    """
+    if sys.platform == 'darwin' and getattr(sys, 'frozen', False):
+        return DarwinProtocolHandler  # pyright: reportUnboundVariable=false
+
+    elif (
+        (sys.platform == 'win32' and config.auth_force_edmc_protocol)
+        or (getattr(sys, 'frozen', False) and not is_wine and not config.auth_force_localserver)
+    ):
+        return WindowsProtocolHandler
+
+    else:
+        return LinuxProtocolHandler
+
+
+# *late init* singleton
 protocolhandler: GenericProtocolHandler
-
-if sys.platform == 'darwin' and getattr(sys, 'frozen', False):
-    protocolhandler = DarwinProtocolHandler()  # pyright: reportUnboundVariable=false
-
-elif (
-        sys.platform == 'win32'
-        and config.auth_force_edmc_protocol or (
-            getattr(sys, 'frozen', False)
-            and not is_wine
-            and not config.auth_force_localserver
-        )
-        ):
-    protocolhandler = WindowsProtocolHandler()
-else:
-    protocolhandler = LinuxProtocolHandler()