From 8fec03bcbf560d4ade6d8ec776d981335dab25cd Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 25 Sep 2016 04:16:07 +0500 Subject: [PATCH 01/11] Add proxy login/pass fields to NetworkSettings class. Use named "constants" instead of hardcoded numbers for proxy "mode" parameter --- service/settings.py | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/service/settings.py b/service/settings.py index cd1b33b44..971ce59ef 100644 --- a/service/settings.py +++ b/service/settings.py @@ -113,6 +113,12 @@ class Settings(): class NetworkSettings(): _instance = None + + # constants for serviceNetworkDefaultSettings["mode"] parameter + PROXY_MODE_NONE = 0 # 0 - No proxy + PROXY_MODE_AUTODETECT = 1 # 1 - Auto-detected proxy settings + PROXY_MODE_MANUAL = 2 # 2 - Manual proxy settings + @classmethod def getInstance(cls): if cls._instance == None: @@ -122,13 +128,18 @@ class NetworkSettings(): def __init__(self): - # mode - # 0 - No proxy - # 1 - Auto-detected proxy settings - # 2 - Manual proxy settings - serviceNetworkDefaultSettings = {"mode": 1, "type": "https", "address": "", "port": "", "access": 15} + serviceNetworkDefaultSettings = { + "mode": self.PROXY_MODE_AUTODETECT, + "type": "https", + "address": "", + "port": "", + "access": 15, + "login": None, + "password": None + } - self.serviceNetworkSettings = SettingsProvider.getInstance().getSettings("pyfaServiceNetworkSettings", serviceNetworkDefaultSettings) + self.serviceNetworkSettings = SettingsProvider.getInstance().getSettings( + "pyfaServiceNetworkSettings", serviceNetworkDefaultSettings) def isEnabled(self, type): if type & self.serviceNetworkSettings["access"]: @@ -198,13 +209,21 @@ class NetworkSettings(): def getProxySettings(self): - if self.getMode() == 0: + if self.getMode() == self.PROXY_MODE_NONE: return None - if self.getMode() == 1: + if self.getMode() == self.PROXY_MODE_AUTODETECT: return self.autodetect() - if self.getMode() == 2: + if self.getMode() == self.PROXY_MODE_MANUAL: return (self.getAddress(), int(self.getPort())) + def getProxyAuthDetails(self): + if self.getMode() == self.PROXY_MODE_NONE: + return None + if (self.serviceNetworkSettings["login"] is None) or (self.serviceNetworkSettings["password"] is None): + return None + # in all other cases, return tuple of (login, password) + return (self.serviceNetworkSettings["login"], self.serviceNetworkSettings["password"]) + """ From a0359b8bd982eb375cd442e9fbfda5981cf6a596 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 28 Sep 2016 01:42:59 +0500 Subject: [PATCH 02/11] [WIP] Add basic proxy authorization support to Network service class. not tested yet, will test later --- service/network.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/service/network.py b/service/network.py index 04ba49875..be0380e82 100644 --- a/service/network.py +++ b/service/network.py @@ -75,8 +75,23 @@ class Network(): proxy = NetworkSettings.getInstance().getProxySettings() if proxy is not None: - proxy = urllib2.ProxyHandler({'https': "{0}:{1}".format(*proxy)}) - opener = urllib2.build_opener(proxy) + # proxy is tuple of (host, port): (u'192.168.20.1', 3128) + proxy_handler = urllib2.ProxyHandler({'https': "{0}:{1}".format(proxy[0], proxy[1])}) + proxy_auth = NetworkSettings.getInstance().getProxyAuthDetails() + if proxy_auth is not None: + # if we have proxy login/pass configured, construct a different opener, + # which uses both proxy handler *and* proxy auth handler + password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + # A realm of None is considered a catch-all realm, which is searched if no other realm fits. + password_mgr.add_password(realm=None, + uri='https://{0}:{1}'.format(proxy[0], proxy[1]), # TODO: is uri correct? + user=proxy_auth[0], + passwd=proxy_auth[1]) + proxy_auth_handler = urllib2.ProxyBasicAuthHandler(password_mgr) + opener = urllib2.build_opener(proxy_handler, proxy_auth_handler) + else: + # With no proxy login/pass provided, use opener with the only proxy handler + opener = urllib2.build_opener(proxy_handler) urllib2.install_opener(opener) request = urllib2.Request(url, headers=headers, data=urllib.urlencode(data) if data else None) From 75f07afcb7d6589fb2ae02e65c5f98b1d9a0e8bf Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Thu, 29 Sep 2016 20:21:11 +0500 Subject: [PATCH 03/11] NetworkSettings: add setter method for proxy auth details (login, password) --- service/settings.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/service/settings.py b/service/settings.py index 971ce59ef..a1607d150 100644 --- a/service/settings.py +++ b/service/settings.py @@ -224,6 +224,14 @@ class NetworkSettings(): # in all other cases, return tuple of (login, password) return (self.serviceNetworkSettings["login"], self.serviceNetworkSettings["password"]) + def setProxyAuthDetails(self, login, password): + if (login is None) or (password is None): + self.serviceNetworkSettings["login"] = None + self.serviceNetworkSettings["password"] = None + return + self.serviceNetworkSettings["login"] = login + self.serviceNetworkSettings["password"] = password + """ From 02557701f04df125027fd990d7d9df6090f1c8e8 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sat, 1 Oct 2016 13:47:27 +0500 Subject: [PATCH 04/11] NetworkSettings: never return None in proxy auth details getter, return a tuple with empty strings instead --- service/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/settings.py b/service/settings.py index a1607d150..a1fe77572 100644 --- a/service/settings.py +++ b/service/settings.py @@ -218,9 +218,9 @@ class NetworkSettings(): def getProxyAuthDetails(self): if self.getMode() == self.PROXY_MODE_NONE: - return None + return ("", "") # never return none, return tuple with empty strings if (self.serviceNetworkSettings["login"] is None) or (self.serviceNetworkSettings["password"] is None): - return None + return ("", "") # never return none, return tuple with empty strings # in all other cases, return tuple of (login, password) return (self.serviceNetworkSettings["login"], self.serviceNetworkSettings["password"]) From 3a7e343f1c1d7dfd8f9dcc1d0eb22fa009369c0e Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 2 Oct 2016 19:15:32 +0500 Subject: [PATCH 05/11] Network preferences settings: GUI to set proxy login/password --- .../pyfaNetworkPreferences.py | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index b4c0dedae..c2767ca31 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -69,6 +69,7 @@ class PFNetworkPref ( PreferenceView): self.nAddr = self.settings.getAddress() self.nPort = self.settings.getPort() self.nType = self.settings.getType() + self.nAuth = self.settings.getProxyAuthDetails() # tuple of (login, password) ptypeSizer = wx.BoxSizer( wx.HORIZONTAL ) @@ -111,6 +112,21 @@ class PFNetworkPref ( PreferenceView): mainSizer.Add( fgAddrSizer, 0, wx.EXPAND, 5) + # proxy auth information: login and pass + self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, u"Proxy login:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetLogin.Wrap(-1) + self.editProxySettingsLogin = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[0], wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, u"pass:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetPassword.Wrap(-1) + self.editProxySettingsPassword = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[1], wx.DefaultPosition, + wx.DefaultSize, wx.TE_PASSWORD) + pAuthSizer = wx.BoxSizer(wx.HORIZONTAL) + pAuthSizer.Add(self.stPSetLogin, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + pAuthSizer.Add(self.editProxySettingsLogin, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + pAuthSizer.Add(self.stPSetPassword, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + pAuthSizer.Add(self.editProxySettingsPassword, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + mainSizer.Add(pAuthSizer, 0, wx.EXPAND, 5) + self.stPSAutoDetected = wx.StaticText( panel, wx.ID_ANY, u"Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, 0 ) self.stPSAutoDetected.Wrap( -1 ) mainSizer.Add( self.stPSAutoDetected, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) @@ -138,13 +154,15 @@ class PFNetworkPref ( PreferenceView): self.chProxyType.Bind(wx.EVT_CHOICE, self.OnCHProxyTypeSelect) self.editProxySettingsAddr.Bind(wx.EVT_TEXT, self.OnEditPSAddrText) self.editProxySettingsPort.Bind(wx.EVT_TEXT, self.OnEditPSPortText) + self.editProxySettingsLogin.Bind(wx.EVT_TEXT, self.OnEditPSLoginText) + self.editProxySettingsPassword.Bind(wx.EVT_TEXT, self.OnEditPSPasswordText) self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) self.UpdateApplyButtonState() - if self.nMode is not 2: + if self.nMode is not service.settings.NetworkSettings.PROXY_MODE_MANUAL: # == 2 self.ToggleProxySettings(False) else: self.ToggleProxySettings(True) @@ -180,6 +198,16 @@ class PFNetworkPref ( PreferenceView): self.dirtySettings = True self.UpdateApplyButtonState() + def OnEditPSLoginText(self, event): + self.nAuth = (self.editProxySettingsLogin.GetValue(), self.nAuth[1]) + self.dirtySettings = True + self.UpdateApplyButtonState() + + def OnEditPSPasswordText(self, event): + self.nAuth = (self.nAuth[0], self.editProxySettingsPassword.GetValue()) + self.dirtySettings = True + self.UpdateApplyButtonState() + def OnBtnApply(self, event): self.dirtySettings = False self.UpdateApplyButtonState() @@ -190,6 +218,7 @@ class PFNetworkPref ( PreferenceView): self.settings.setAddress(self.nAddr) self.settings.setPort(self.nPort) self.settings.setType(self.nType) + self.settings.setProxyAuthDetails(self.nAuth[0], self.nAuth[1]) def UpdateApplyButtonState(self): if self.dirtySettings: @@ -205,7 +234,7 @@ class PFNetworkPref ( PreferenceView): self.UpdateApplyButtonState() - if choice is not 2: + if choice is not service.settings.NetworkSettings.PROXY_MODE_MANUAL: self.ToggleProxySettings(False) else: self.ToggleProxySettings(True) @@ -216,11 +245,19 @@ class PFNetworkPref ( PreferenceView): self.editProxySettingsAddr.Enable() self.stPSetPort.Enable() self.editProxySettingsPort.Enable() + self.stPSetLogin.Enable() + self.stPSetPassword.Enable() + self.editProxySettingsLogin.Enable() + self.editProxySettingsPassword.Enable() else: self.stPSetAddr.Disable() self.editProxySettingsAddr.Disable() self.stPSetPort.Disable() self.editProxySettingsPort.Disable() + self.stPSetLogin.Disable() + self.stPSetPassword.Disable() + self.editProxySettingsLogin.Disable() + self.editProxySettingsPassword.Disable() def getImage(self): return BitmapLoader.getBitmap("prefs_proxy", "gui") From c166fa6bf5049b05a70a224fb1ca5774761dd19e Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 2 Oct 2016 22:08:58 +0500 Subject: [PATCH 06/11] service/network: revert to simple ProxyHandler with login:password@host:port format previous sh@t with password managers and proxy basic auth handlers did not work for me :( this way is simpler AND working. Also explicitly use the default urllib2 opener if proxy is disabled (bug fix) --- service/network.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/service/network.py b/service/network.py index be0380e82..3279b0769 100644 --- a/service/network.py +++ b/service/network.py @@ -43,6 +43,7 @@ class ServerError(StandardError): class TimeoutError(StandardError): pass + class Network(): # Request constants - every request must supply this, as it is checked if # enabled or not via settings @@ -71,28 +72,29 @@ class Network(): # Set up some things for the request versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion) - headers={"User-Agent" : "pyfa {0} (Python-urllib2)".format(versionString)} + headers = {"User-Agent" : "pyfa {0} (Python-urllib2)".format(versionString)} proxy = NetworkSettings.getInstance().getProxySettings() if proxy is not None: # proxy is tuple of (host, port): (u'192.168.20.1', 3128) + # build default proxy handler proxy_handler = urllib2.ProxyHandler({'https': "{0}:{1}".format(proxy[0], proxy[1])}) proxy_auth = NetworkSettings.getInstance().getProxyAuthDetails() if proxy_auth is not None: - # if we have proxy login/pass configured, construct a different opener, - # which uses both proxy handler *and* proxy auth handler - password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() - # A realm of None is considered a catch-all realm, which is searched if no other realm fits. - password_mgr.add_password(realm=None, - uri='https://{0}:{1}'.format(proxy[0], proxy[1]), # TODO: is uri correct? - user=proxy_auth[0], - passwd=proxy_auth[1]) - proxy_auth_handler = urllib2.ProxyBasicAuthHandler(password_mgr) - opener = urllib2.build_opener(proxy_handler, proxy_auth_handler) - else: - # With no proxy login/pass provided, use opener with the only proxy handler - opener = urllib2.build_opener(proxy_handler) + # add login:password@ in front of proxy address + new_proxy_address = '{0}:{1}@{2}:{3}'.format(proxy_auth[0], proxy_auth[1], proxy[0], proxy[1]) + proxy_handler = urllib2.ProxyHandler({'https': new_proxy_address}) + opener = urllib2.build_opener(proxy_handler) urllib2.install_opener(opener) + else: + # This is a bug fix, explicitly disable possibly previously installed + # opener with proxy, by urllib2.install_opener() a few lines above in code. + # Now this explicitly disables proxy handler, "uninstalling" opener. + # This is used in case when user had proxy enabled, so proxy_handler was already + # installed globally, and then user had disabled the proxy, so we should clear that opener + urllib2.install_opener(None) + # another option could be installing a default opener: + # urllib2.install_opener(urllib2.build_opener()) request = urllib2.Request(url, headers=headers, data=urllib.urlencode(data) if data else None) try: From 2a9a4fbdff21fe61d1155f1f7ca85ccbff334aa2 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 9 Oct 2016 13:05:39 +0500 Subject: [PATCH 07/11] service/settings: make getProxyAuthDetails() return None again --- service/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/settings.py b/service/settings.py index a1fe77572..a1607d150 100644 --- a/service/settings.py +++ b/service/settings.py @@ -218,9 +218,9 @@ class NetworkSettings(): def getProxyAuthDetails(self): if self.getMode() == self.PROXY_MODE_NONE: - return ("", "") # never return none, return tuple with empty strings + return None if (self.serviceNetworkSettings["login"] is None) or (self.serviceNetworkSettings["password"] is None): - return ("", "") # never return none, return tuple with empty strings + return None # in all other cases, return tuple of (login, password) return (self.serviceNetworkSettings["login"], self.serviceNetworkSettings["password"]) From 1e9f911385e341f639374f06d40c0a2072eab2db Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 9 Oct 2016 13:07:13 +0500 Subject: [PATCH 08/11] service/settings: setProxyAuthDetails(): empty string as login means no password too --- service/settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/service/settings.py b/service/settings.py index a1607d150..d88354f12 100644 --- a/service/settings.py +++ b/service/settings.py @@ -229,6 +229,10 @@ class NetworkSettings(): self.serviceNetworkSettings["login"] = None self.serviceNetworkSettings["password"] = None return + if login == "": # empty login unsets proxy auth info + self.serviceNetworkSettings["login"] = None + self.serviceNetworkSettings["password"] = None + return self.serviceNetworkSettings["login"] = login self.serviceNetworkSettings["password"] = password From 2b5535c5d1a1fb23f59718ca99f74fafcf5c6ff1 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 9 Oct 2016 13:14:37 +0500 Subject: [PATCH 09/11] service/network: change "if" condition as suggested by @blitzmann --- service/network.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/service/network.py b/service/network.py index 3279b0769..f7383e03d 100644 --- a/service/network.py +++ b/service/network.py @@ -76,14 +76,16 @@ class Network(): proxy = NetworkSettings.getInstance().getProxySettings() if proxy is not None: - # proxy is tuple of (host, port): (u'192.168.20.1', 3128) - # build default proxy handler - proxy_handler = urllib2.ProxyHandler({'https': "{0}:{1}".format(proxy[0], proxy[1])}) + # proxy is a tuple of (host, port): (u'192.168.20.1', 3128) proxy_auth = NetworkSettings.getInstance().getProxyAuthDetails() + # proxy_auth is a tuple of (login, password) or None if proxy_auth is not None: # add login:password@ in front of proxy address - new_proxy_address = '{0}:{1}@{2}:{3}'.format(proxy_auth[0], proxy_auth[1], proxy[0], proxy[1]) - proxy_handler = urllib2.ProxyHandler({'https': new_proxy_address}) + proxy_handler = urllib2.ProxyHandler({'https': '{0}:{1}@{2}:{3}'.format( + proxy_auth[0], proxy_auth[1], proxy[0], proxy[1])}) + else: + # build proxy handler with no login/pass info + proxy_handler = urllib2.ProxyHandler({'https': "{0}:{1}".format(proxy[0], proxy[1])}) opener = urllib2.build_opener(proxy_handler) urllib2.install_opener(opener) else: From 160de64135ea3d1bd2bbd8f95f65e4dc535ff426 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 9 Oct 2016 13:16:38 +0500 Subject: [PATCH 10/11] Network settings GUI: be prepared for None return from getProxyAuthDetails() --- gui/builtinPreferenceViews/pyfaNetworkPreferences.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index c2767ca31..042237214 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -70,6 +70,8 @@ class PFNetworkPref ( PreferenceView): self.nPort = self.settings.getPort() self.nType = self.settings.getType() self.nAuth = self.settings.getProxyAuthDetails() # tuple of (login, password) + if self.nAuth is None: + self.nAuth = ("", "") # we don't want None here, it should be a tuple ptypeSizer = wx.BoxSizer( wx.HORIZONTAL ) From a06810c7c8dfe4462bfd70000866e7d3d3d66fcf Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 9 Oct 2016 13:20:20 +0500 Subject: [PATCH 11/11] Network settings GUI: Use "Username:" and "Password:" for labels, as you wish --- gui/builtinPreferenceViews/pyfaNetworkPreferences.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index 042237214..bd79dd88c 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -115,10 +115,10 @@ class PFNetworkPref ( PreferenceView): mainSizer.Add( fgAddrSizer, 0, wx.EXPAND, 5) # proxy auth information: login and pass - self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, u"Proxy login:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, u"Username:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetLogin.Wrap(-1) self.editProxySettingsLogin = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[0], wx.DefaultPosition, wx.DefaultSize, 0) - self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, u"pass:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, u"Password:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetPassword.Wrap(-1) self.editProxySettingsPassword = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[1], wx.DefaultPosition, wx.DefaultSize, wx.TE_PASSWORD)