From cffebf2f65f2a20ec861692f071b3c3e31b78e77 Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 28 Jan 2019 14:26:42 +0100 Subject: [PATCH] Better, safer dialogs to enter network credentials --- default.py | 3 +- .../resource.language.en_gb/strings.po | 30 +++ resources/lib/initialsetup.py | 3 +- resources/lib/utils.py | 121 ------------ resources/lib/windows/direct_path_sources.py | 180 ++++++++++++++++++ 5 files changed, 214 insertions(+), 123 deletions(-) create mode 100644 resources/lib/windows/direct_path_sources.py diff --git a/default.py b/default.py index 5668a751..e4e549f8 100644 --- a/default.py +++ b/default.py @@ -95,7 +95,8 @@ class Main(): entrypoint.toggle_plex_tv_sign_in() elif mode == 'passwords': - utils.passwords_xml() + from resources.lib.windows import direct_path_sources + direct_path_sources.start() elif mode == 'switchuser': entrypoint.switch_plex_user() diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 7cadbaa5..a5f341f2 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -306,6 +306,36 @@ msgctxt "#30198" msgid "Search" msgstr "" +# For setting up direct paths and adding network credentials +msgctxt "#30200" +msgid "In the following window, enter the server's hostname (or IP) where your Plex media resides. Mind the case!" +msgstr "" + +# For setting up direct paths and adding network credentials - input window for hostname +msgctxt "#30201" +msgid "Enter server hostname (or IP)" +msgstr "" + +# For setting up direct paths and adding network credentials +msgctxt "#30202" +msgid "In the following window, enter the network protocol you would like to use. This is likely 'smb'." +msgstr "" + +# For setting up direct paths and adding network credentials - input window protocol +msgctxt "#30203" +msgid "Enter network protocol" +msgstr "" + +# For setting up direct paths and adding network credentials +msgctxt "#30204" +msgid "The hostname or IP '{0}' that you entered is not valid." +msgstr "" + +# For setting up direct paths and adding network credentials +msgctxt "#30205" +msgid "The protocol '{0}' that you entered is not supported." +msgstr "" + # Video node naming for random e.g. movies msgctxt "#30227" msgid "Random" diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index ac815994..4dcda83f 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -602,7 +602,8 @@ class InitialSetup(object): # Go to network credentials? if utils.yesno_dialog(utils.lang(39029), utils.lang(39030)): LOG.debug("Presenting network credentials dialog.") - utils.passwords_xml() + from .windows import direct_path_sources + direct_path_sources.start() # Disable Plex music? if utils.yesno_dialog(utils.lang(29999), utils.lang(39016)): LOG.debug("User opted to disable Plex music library.") diff --git a/resources/lib/utils.py b/resources/lib/utils.py index c7bddd97..56ac9f50 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -14,7 +14,6 @@ import xml.etree.ElementTree as etree import defusedxml.ElementTree as defused_etree # etree parse unsafe from xml.etree.ElementTree import ParseError from functools import wraps -from urllib import quote_plus import hashlib import re import gc @@ -824,126 +823,6 @@ class XmlKodiSetting(object): return element -def passwords_xml(): - """ - To add network credentials to Kodi's password xml - """ - path = path_ops.translate_path('special://userdata/') - xmlpath = "%spasswords.xml" % path - try: - xmlparse = defused_etree.parse(xmlpath) - except IOError: - # Document is blank or missing - root = etree.Element('passwords') - skip_find = True - except ParseError: - LOG.error('Error parsing %s', xmlpath) - # "Kodi cannot parse {0}. PKC will not function correctly. Please visit - # {1} and correct your file!" - messageDialog(lang(29999), lang(39716).format( - 'passwords.xml', 'http://forum.kodi.tv/')) - return - else: - root = xmlparse.getroot() - skip_find = False - - credentials = settings('networkCreds') - if credentials: - # Present user with options - option = dialog('select', - "Modify/Remove network credentials", - ["Modify", "Remove"]) - - if option < 0: - # User cancelled dialog - return - - elif option == 1: - # User selected remove - success = False - for paths in root.getiterator('passwords'): - for path in paths: - if path.find('.//from').text == "smb://%s/" % credentials: - paths.remove(path) - LOG.info("Successfully removed credentials for: %s", - credentials) - etree.ElementTree(root).write(xmlpath, - encoding="UTF-8") - success = True - if not success: - LOG.error("Failed to find saved server: %s in passwords.xml", - credentials) - dialog('notification', - heading='{plex}', - message="%s not found" % credentials, - icon='{warning}', - sound=False) - return - settings('networkCreds', value="") - dialog('notification', - heading='{plex}', - message="%s removed from passwords.xml" % credentials, - icon='{plex}', - sound=False) - return - - elif option == 0: - # User selected to modify - server = dialog('input', - "Modify the computer name or ip address", - credentials) - if not server: - return - else: - # No credentials added - messageDialog("Network credentials", - 'Input the server name or IP address as indicated in your plex ' - 'library paths. For example, the server name: ' - '\\\\SERVER-PC\\path\\ or smb://SERVER-PC/path is SERVER-PC') - server = dialog('input', "Enter the server name or IP address") - if not server: - return - server = quote_plus(server) - - # Network username - user = dialog('input', "Enter the network username") - if not user: - return - user = quote_plus(user) - # Network password - password = dialog('input', - "Enter the network password", - '', # Default input - type='{alphanum}', - option='{hide}') - # Need to url-encode the password - password = quote_plus(password) - # Add elements. Annoying etree bug where findall hangs forever - if skip_find is False: - skip_find = True - for path in root.findall('.//path'): - if path.find('.//from').text.lower() == "smb://%s/" % server.lower(): - # Found the server, rewrite credentials - path.find('.//to').text = ("smb://%s:%s@%s/" - % (user, password, server)) - skip_find = False - break - if skip_find: - # Server not found, add it. - path = etree.SubElement(root, 'path') - etree.SubElement(path, 'from', {'pathversion': "1"}).text = \ - "smb://%s/" % server - topath = "smb://%s:%s@%s/" % (user, password, server) - etree.SubElement(path, 'to', {'pathversion': "1"}).text = topath - - # Add credentials - settings('networkCreds', value="%s" % server) - LOG.info("Added server: %s to passwords.xml", server) - # Prettify and write to file - indent(root) - etree.ElementTree(root).write(xmlpath, encoding="UTF-8") - - def playlist_xsp(mediatype, tagname, viewid, viewtype="", delete=False): """ Feed with tagname as unicode diff --git a/resources/lib/windows/direct_path_sources.py b/resources/lib/windows/direct_path_sources.py new file mode 100644 index 00000000..888e9667 --- /dev/null +++ b/resources/lib/windows/direct_path_sources.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +:module: plexkodiconnect.userselect +:synopsis: Prompts the user to add network paths and username passwords for + e.g. smb paths +""" +from __future__ import absolute_import, division, unicode_literals +from logging import getLogger +import re +import socket +import urllib + +import xbmc + +from .. import path_ops, utils + +LOG = getLogger('PLEX.direct_path_sources') + +SUPPORTED_PROTOCOLS = ('smb', 'nfs', 'http', 'https', 'ftp', 'sftp') +PATH = path_ops.translate_path('special://userdata/') + + +def get_etree(topelement): + try: + xml = utils.defused_etree.parse( + path_ops.path.join(PATH, '%s.xml' % topelement)) + except IOError: + # Document is blank or missing + LOG.info('%s.xml is missing or blank, creating it', topelement) + root = utils.etree.Element(topelement) + except utils.ParseError: + LOG.error('Error parsing %s', topelement) + # "Kodi cannot parse {0}. PKC will not function correctly. Please visit + # {1} and correct your file!" + utils.messageDialog(utils.lang(29999), utils.lang(39716).format( + '%s.xml' % topelement, 'http://forum.kodi.tv/')) + return + else: + root = xml.getroot() + return root + + +def is_valid_hostname(hostname): + if len(hostname) > 255: + return False + if hostname[-1] == ".": + # strip exactly one dot from the right, if present + hostname = hostname[:-1] + allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?