diff --git a/syncplay/client.py b/syncplay/client.py index 36d9632..fb769c6 100755 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -9,9 +9,9 @@ import re import sys import threading import time -from fnmatch import fnmatch from copy import deepcopy from functools import wraps +from urllib.parse import urlparse from twisted.application.internet import ClientService from twisted.internet.endpoints import HostnameEndpoint @@ -546,27 +546,47 @@ class SyncplayClient(object): if oldRoomList != newRoomList: self._config['roomList'] = newRoomList + def _isURITrustableAndTrusted(self, URIToTest): + """Returns a tuple of booleans: (trustable, trusted). + + A given URI is "trustable" if it uses HTTP or HTTPS (constants.TRUSTABLE_WEB_PROTOCOLS). + A given URI is "trusted" if it matches an entry in the trustedDomains config. + Such an entry is considered matching if the domain is the same and the path + is a prefix of the given URI's path. + A "trustable" URI is always "trusted" if the config onlySwitchToTrustedDomains is false. + """ + o = urlparse(URIToTest) + trustable = o.scheme in constants.TRUSTABLE_WEB_PROTOCOLS + if not trustable: + # untrustable URIs are never trusted, return early + return False, False + if not self._config['onlySwitchToTrustedDomains']: + # trust all trustable URIs in this case + return trustable, True + # check for matching trusted domains + if self._config['trustedDomains']: + for entry in self._config['trustedDomains']: + trustedDomain, _, path = entry.partition('/') + if o.hostname not in (trustedDomain, "www." + trustedDomain): + # domain does not match + continue + if path and not o.path.startswith('/' + path): + # trusted domain has a path component and it does not match + continue + # match found, trust this domain + return trustable, True + # no matches found, do not trust this domain + return trustable, False + def isUntrustedTrustableURI(self, URIToTest): if utils.isURL(URIToTest): - for trustedProtocol in constants.TRUSTABLE_WEB_PROTOCOLS: - if URIToTest.startswith(trustedProtocol) and not self.isURITrusted(URIToTest): - return True + trustable, trusted = self._isURITrustableAndTrusted(URIToTest) + return trustable and not trusted return False def isURITrusted(self, URIToTest): - URIToTest = URIToTest+"/" - for trustedProtocol in constants.TRUSTABLE_WEB_PROTOCOLS: - if URIToTest.startswith(trustedProtocol): - if self._config['onlySwitchToTrustedDomains']: - if self._config['trustedDomains']: - for trustedDomain in self._config['trustedDomains']: - trustableURI = ''.join([trustedProtocol, trustedDomain, "/*"]) - if fnmatch(URIToTest, trustableURI): - return True - return False - else: - return True - return False + trustable, trusted = self._isURITrustableAndTrusted(URIToTest) + return trustable and trusted def openFile(self, filePath, resetPosition=False, fromUser=False): if fromUser and filePath.endswith(".txt") or filePath.endswith(".m3u") or filePath.endswith(".m3u8"): diff --git a/syncplay/constants.py b/syncplay/constants.py index 171e22f..b9c0412 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -345,6 +345,6 @@ SYNCPLAY_DOWNLOAD_URL = "https://syncplay.pl/download/" SYNCPLAY_PUBLIC_SERVER_LIST_URL = "https://syncplay.pl/listpublicservers?{}" # Params DEFAULT_TRUSTED_DOMAINS = ["youtube.com", "youtu.be"] -TRUSTABLE_WEB_PROTOCOLS = ["http://www.", "https://www.", "http://", "https://"] +TRUSTABLE_WEB_PROTOCOLS = ["http", "https"] PRIVATE_FILE_FIELDS = ["path"] diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index a4884ef..f2195e9 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -731,7 +731,8 @@ class MainWindow(QtWidgets.QMainWindow): lambda: utils.open_system_file_browser(pathFound)) if self._syncplayClient.isUntrustedTrustableURI(firstFile): domain = utils.getDomainFromURL(firstFile) - menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"), getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) + if domain: + menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"), getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) menu.addAction(QtGui.QPixmap(resourcespath + "delete.png"), getMessage("removefromplaylist-menu-label"), lambda: self.deleteSelectedPlaylistItems()) menu.addSeparator() menu.addAction(QtGui.QPixmap(resourcespath + "arrow_switch.png"), getMessage("shuffleremainingplaylist-menu-label"), lambda: self.shuffleRemainingPlaylist()) @@ -794,7 +795,8 @@ class MainWindow(QtWidgets.QMainWindow): menu.addAction(QtGui.QPixmap(resourcespath + "film_go.png"), getMessage("openusersfile-menu-label").format(shortUsername), lambda: self.openFile(pathFound, resetPosition=False, fromUser=True)) if self._syncplayClient.isUntrustedTrustableURI(filename): domain = utils.getDomainFromURL(filename) - menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"), getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) + if domain: + menu.addAction(QtGui.QPixmap(resourcespath + "shield_add.png"), getMessage("addtrusteddomain-menu-label").format(domain), lambda: self.addTrustedDomain(domain)) if not isURL(filename) and filename != getMessage("nofile-note"): path = self._syncplayClient.fileSwitch.findFilepath(filename) diff --git a/syncplay/utils.py b/syncplay/utils.py index 5a394d3..bfc788c 100755 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -395,12 +395,15 @@ def playlistIsValid(files): def getDomainFromURL(URL): try: - URL = URL.split("//")[-1].split("/")[0] - if URL.startswith("www."): - URL = URL[4:] - return URL - except: + o = urllib.parse.urlparse(URL) + except ValueError: + # not a URL return None + if o.hostname is not None and o.hostname.startswith("www."): + return o.hostname[4:] + else: + # may return None if URL does not have domain (invalid url) + return o.hostname def open_system_file_browser(path):