From 01609e4d72cdd3034e971f5ebfb40d3f9561d730 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 3 Feb 2019 16:44:37 +0100 Subject: [PATCH] Better inform the user if problems arise with manual PMS IP input --- .../resource.language.en_gb/strings.po | 5 ++ resources/lib/downloadutils.py | 31 ++++++-- resources/lib/initialsetup.py | 77 +++++++++++++------ resources/lib/plex_functions.py | 14 +++- resources/lib/service_entry.py | 31 ++++++-- 5 files changed, 119 insertions(+), 39 deletions(-) diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index b98f74bd..a4c04828 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -464,6 +464,11 @@ msgctxt "#30502" msgid "Sync Plex artwork from the PMS (recommended)" msgstr "" +# Message shown if SSL HTTPS certificate fails +msgctxt "#30503" +msgid "SSL certificate failed to validate. Please check {0} for solutions." +msgstr "" + # PKC Settings, category name msgctxt "#30506" msgid "Sync Options" diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index b4bc2562..77491c64 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, division, unicode_literals from logging import getLogger import requests +import requests.exceptions as exceptions from . import utils, clientinfo, app @@ -114,7 +115,7 @@ class DownloadUtils(): def downloadUrl(self, url, action_type="GET", postBody=None, parameters=None, authenticate=True, headerOptions=None, verifySSL=True, timeout=None, return_response=False, - headerOverride=None): + headerOverride=None, reraise=False): """ Override SSL check with verifySSL=False @@ -172,39 +173,55 @@ class DownloadUtils(): r = self._doDownload(s, action_type, **kwargs) # THE EXCEPTIONS - except requests.exceptions.SSLError as e: + except exceptions.SSLError as e: LOG.warn("Invalid SSL certificate for: %s", url) LOG.warn(e) + if reraise: + raise - except requests.exceptions.ConnectionError as e: + except exceptions.ConnectionError as e: # Connection error LOG.warn("Server unreachable at: %s", url) LOG.warn(e) + if reraise: + raise - except requests.exceptions.Timeout as e: + except exceptions.Timeout as e: LOG.warn("Server timeout at: %s", url) LOG.warn(e) + if reraise: + raise - except requests.exceptions.HTTPError as e: + except exceptions.HTTPError as e: LOG.warn('HTTP Error at %s', url) LOG.warn(e) + if reraise: + raise - except requests.exceptions.TooManyRedirects as e: + except exceptions.TooManyRedirects as e: LOG.warn("Too many redirects connecting to: %s", url) LOG.warn(e) + if reraise: + raise - except requests.exceptions.RequestException as e: + except exceptions.RequestException as e: LOG.warn("Unknown error connecting to: %s", url) LOG.warn(e) + if reraise: + raise except SystemExit: LOG.info('SystemExit detected, aborting download') self.stopSession() + if reraise: + raise except: LOG.warn('Unknown error while downloading. Traceback:') import traceback LOG.warn(traceback.format_exc()) + if reraise: + raise # THE RESPONSE ##### else: diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 269fa7a6..78fc590b 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -9,7 +9,7 @@ from . import utils from .utils import etree from . import path_ops from . import migration -from .downloadutils import DownloadUtils as DU +from .downloadutils import DownloadUtils as DU, exceptions from . import plex_functions as PF from . import plex_tv from . import json_rpc as js @@ -92,28 +92,59 @@ class InitialSetup(object): else: url = 'http://%s' % url https = 'true' if https else 'false' - machine_identifier = PF.GetMachineIdentifier(url) - if machine_identifier is None: - LOG.error('Could not get machine identifier') - # "Error contacting url - # Abort (Yes) or save address anyway (No)" - if utils.yesno_dialog(utils.lang(29999), - '%s %s. %s' % (utils.lang(39218), - url, - utils.lang(39219))): - return False - else: - LOG.info('Saving manual address anyway') - utils.settings('plex_machineIdentifier', '') - else: - utils.settings('plex_machineIdentifier', machine_identifier) - LOG.info('Set new PMS to https %s, address %s, port %s, machineId %s', - https, address, port, machine_identifier) - utils.settings('https', value=https) - utils.settings('ipaddress', value=address) - utils.settings('port', value=port) - # Chances are this is a local PMS, so disable SSL certificate check - utils.settings('sslverify', value='false') + # Try to connect first + error = False + try: + machine_identifier = PF.GetMachineIdentifier(url) + except exceptions.SSLError: + LOG.error('SSL cert error contacting %s', url) + # "SSL certificate failed to validate. Please check {0} + # for solutions." + utils.messageDialog(utils.lang(29999), + utils.lang(30503).format('github.com/croneter/PlexKodiConnect/issues')) + return + except Exception: + error = True + if error or machine_identifier is None: + LOG.error('Could not even get a machineIdentifier for %s', url) + # "Server is unreachable" + utils.messageDialog(utils.lang(29999), utils.lang(33002)) + return + # Let's use the main account's token, not managed user token + token = utils.settings('plexToken') + xml = PF.pms_root(url, token) + if xml == 401: + LOG.error('Not yet authorized for %s', url) + # "User is unauthorized for server {0}", + # "Please sign in to plex.tv." + utils.messageDialog(utils.lang(29999), + '%s. %s' % (utils.lang(33010).format(address), + utils.lang(39014))) + return + try: + xml[0].attrib + except (IndexError, TypeError, AttributeError): + LOG.error('Could not get PMS root directory for %s', url) + # "Error contacting PMS" + utils.messageDialog(utils.lang(29999), utils.lang(39218)) + return + pms = { + 'baseURL': url, + 'ip': address, + # Assume PMS is not local so we're not resetting verifyssl + 'local': False, + 'machineIdentifier': xml.get('machineIdentifier'), + 'name': xml.get('friendlyName'), + # Assume that we own this PMS - no easy way to check + 'owned': True, + 'platform': xml.get('platform'), + 'port': port, + # 'relay': True, + 'scheme': 'https' if https else 'http', + 'token': token, + 'version': xml.get('version') + } + return pms def plex_tv_sign_in(self): """ diff --git a/resources/lib/plex_functions.py b/resources/lib/plex_functions.py index b006dc10..6d29609e 100644 --- a/resources/lib/plex_functions.py +++ b/resources/lib/plex_functions.py @@ -834,7 +834,8 @@ def GetMachineIdentifier(url): xml = DU().downloadUrl('%s/identity' % url, authenticate=False, verifySSL=False, - timeout=10) + timeout=10, + reraise=True) try: machineIdentifier = xml.attrib['machineIdentifier'] except (AttributeError, KeyError): @@ -937,6 +938,17 @@ def delete_item_from_pms(plexid): return False +def pms_root(url, token): + """ + Retrieve the PMS' most basic settings by retrieving / + """ + return DU().downloadUrl( + url, + authenticate=False, + verifySSL=True if v.KODIVERSION >= 18 else False, + headerOptions={'X-Plex-Token': token} if token else None) + + def get_PMS_settings(url, token): """ Retrieve the PMS' settings via /:/prefs diff --git a/resources/lib/service_entry.py b/resources/lib/service_entry.py index f59ed943..63b4e554 100644 --- a/resources/lib/service_entry.py +++ b/resources/lib/service_entry.py @@ -269,14 +269,29 @@ class Service(): self.auth_running = False def enter_new_pms_address(self): - if self.setup.enter_new_pms_address(): - app.CONN.load() - app.ACCOUNT.reset_session() - app.ACCOUNT.set_unauthenticated() - self.server_has_been_online = False - self.welcome_msg = False - # Force a full sync - app.SYNC.run_lib_scan = 'full' + server = self.setup.enter_new_pms_address() + if not server: + return + if not self.log_out(): + return False + # Save changes to to file + self.setup.save_pms_settings(server['baseURL'], server['token']) + self.setup.write_pms_to_settings(server) + if not v.KODIVERSION >= 18: + utils.settings('sslverify', value='false') + if not self.log_out(): + return False + # Wipe Kodi and Plex database as well as playlists and video nodes + utils.wipe_database() + app.CONN.load() + app.ACCOUNT.reset_session() + app.ACCOUNT.set_unauthenticated() + self.server_has_been_online = False + self.welcome_msg = False + # Force a full sync + app.SYNC.run_lib_scan = 'full' + LOG.info("Choosing new PMS complete") + return True def _do_auth(self): LOG.info('Authenticating user')