Exemplo n.º 1
0
class JsEngine(QObject):
    def __init__(self):
        QObject.__init__(self)
        self.engine = QJSEngine()
        t = self.engine.newQObject(self)
        self.engine.globalObject().setProperty("app", t)

    @pyqtSlot(str)
    def exec_js(self, js):
        val = self.engine.evaluate(js)
        if not val.isNull() and not val.isUndefined():
            print("error:{0}".format(val.toString()))

    @pyqtSlot()
    def exec_js1(self):
        js = """
            var c = 123;
            """
        self.exec_js(js)

    @pyqtSlot(str, str)
    def log(self, p1, p2):
        print(p1, p2)
Exemplo n.º 2
0
class PACResolver:
    """Evaluate PAC script files and resolve proxies."""
    @staticmethod
    def _parse_proxy_host(host_str):
        host, _colon, port_str = host_str.partition(':')
        try:
            port = int(port_str)
        except ValueError:
            raise ParseProxyError("Invalid port number")
        return (host, port)

    @staticmethod
    def _parse_proxy_entry(proxy_str):
        """Parse one proxy string entry, as described in PAC specification."""
        config = [c.strip() for c in proxy_str.split(' ') if c]
        if not config:
            raise ParseProxyError("Empty proxy entry")
        elif config[0] == "DIRECT":
            if len(config) != 1:
                raise ParseProxyError("Invalid number of parameters for " +
                                      "DIRECT")
            return QNetworkProxy(QNetworkProxy.NoProxy)
        elif config[0] == "PROXY":
            if len(config) != 2:
                raise ParseProxyError("Invalid number of parameters for PROXY")
            host, port = PACResolver._parse_proxy_host(config[1])
            return QNetworkProxy(QNetworkProxy.HttpProxy, host, port)
        elif config[0] in ["SOCKS", "SOCKS5"]:
            if len(config) != 2:
                raise ParseProxyError("Invalid number of parameters for SOCKS")
            host, port = PACResolver._parse_proxy_host(config[1])
            return QNetworkProxy(QNetworkProxy.Socks5Proxy, host, port)
        else:
            err = "Unknown proxy type: {}"
            raise ParseProxyError(err.format(config[0]))

    @staticmethod
    def _parse_proxy_string(proxy_str):
        proxies = proxy_str.split(';')
        return [PACResolver._parse_proxy_entry(x) for x in proxies]

    def _evaluate(self, js_code, js_file):
        ret = self._engine.evaluate(js_code, js_file)
        if ret.isError():
            err = "JavaScript error while evaluating PAC file: {}"
            raise EvalProxyError(err.format(ret.toString()))

    def __init__(self, pac_str):
        """Create a PAC resolver.

        Args:
            pac_str: JavaScript code containing PAC resolver.
        """
        self._engine = QJSEngine()

        self._ctx = _PACContext(self._engine)
        self._engine.globalObject().setProperty(
            "PAC", self._engine.newQObject(self._ctx))
        self._evaluate(_PACContext.JS_DEFINITIONS, "pac_js_definitions")
        self._evaluate(utils.read_file("javascript/pac_utils.js"), "pac_utils")
        proxy_config = self._engine.newObject()
        proxy_config.setProperty("bindings", self._engine.newObject())
        self._engine.globalObject().setProperty("ProxyConfig", proxy_config)

        self._evaluate(pac_str, "pac")
        global_js_object = self._engine.globalObject()
        self._resolver = global_js_object.property("FindProxyForURL")
        if not self._resolver.isCallable():
            err = "Cannot resolve FindProxyForURL function, got '{}' instead"
            raise EvalProxyError(err.format(self._resolver.toString()))

    def resolve(self, query, from_file=False):
        """Resolve a proxy via PAC.

        Args:
            query: QNetworkProxyQuery.
            from_file: Whether the proxy info is coming from a file.

        Return:
            A list of QNetworkProxy objects in order of preference.
        """
        if from_file:
            string_flags = QUrl.PrettyDecoded
        else:
            string_flags = QUrl.RemoveUserInfo
            if query.url().scheme() == 'https':
                string_flags |= QUrl.RemovePath | QUrl.RemoveQuery

        result = self._resolver.call(
            [query.url().toString(string_flags),
             query.peerHostName()])
        result_str = result.toString()
        if not result.isString():
            err = "Got strange value from FindProxyForURL: '{}'"
            raise EvalProxyError(err.format(result_str))
        return self._parse_proxy_string(result_str)
Exemplo n.º 3
0
class PACResolver:

    """Evaluate PAC script files and resolve proxies."""

    @staticmethod
    def _parse_proxy_host(host_str):
        host, _colon, port_str = host_str.partition(':')
        try:
            port = int(port_str)
        except ValueError:
            raise ParseProxyError("Invalid port number")
        return (host, port)

    @staticmethod
    def _parse_proxy_entry(proxy_str):
        """Parse one proxy string entry, as described in PAC specification."""
        config = [c.strip() for c in proxy_str.split(' ') if c]
        if not config:
            raise ParseProxyError("Empty proxy entry")
        elif config[0] == "DIRECT":
            if len(config) != 1:
                raise ParseProxyError("Invalid number of parameters for " +
                                      "DIRECT")
            return QNetworkProxy(QNetworkProxy.NoProxy)
        elif config[0] == "PROXY":
            if len(config) != 2:
                raise ParseProxyError("Invalid number of parameters for PROXY")
            host, port = PACResolver._parse_proxy_host(config[1])
            return QNetworkProxy(QNetworkProxy.HttpProxy, host, port)
        elif config[0] in ["SOCKS", "SOCKS5"]:
            if len(config) != 2:
                raise ParseProxyError("Invalid number of parameters for SOCKS")
            host, port = PACResolver._parse_proxy_host(config[1])
            return QNetworkProxy(QNetworkProxy.Socks5Proxy, host, port)
        else:
            err = "Unknown proxy type: {}"
            raise ParseProxyError(err.format(config[0]))

    @staticmethod
    def _parse_proxy_string(proxy_str):
        proxies = proxy_str.split(';')
        return [PACResolver._parse_proxy_entry(x) for x in proxies]

    def _evaluate(self, js_code, js_file):
        ret = self._engine.evaluate(js_code, js_file)
        if ret.isError():
            err = "JavaScript error while evaluating PAC file: {}"
            raise EvalProxyError(err.format(ret.toString()))

    def __init__(self, pac_str):
        """Create a PAC resolver.

        Args:
            pac_str: JavaScript code containing PAC resolver.
        """
        self._engine = QJSEngine()

        self._ctx = _PACContext(self._engine)
        self._engine.globalObject().setProperty(
            "PAC", self._engine.newQObject(self._ctx))
        self._evaluate(_PACContext.JS_DEFINITIONS, "pac_js_definitions")
        self._evaluate(utils.read_file("javascript/pac_utils.js"), "pac_utils")
        proxy_config = self._engine.newObject()
        proxy_config.setProperty("bindings", self._engine.newObject())
        self._engine.globalObject().setProperty("ProxyConfig", proxy_config)

        self._evaluate(pac_str, "pac")
        global_js_object = self._engine.globalObject()
        self._resolver = global_js_object.property("FindProxyForURL")
        if not self._resolver.isCallable():
            err = "Cannot resolve FindProxyForURL function, got '{}' instead"
            raise EvalProxyError(err.format(self._resolver.toString()))

    def resolve(self, query, from_file=False):
        """Resolve a proxy via PAC.

        Args:
            query: QNetworkProxyQuery.
            from_file: Whether the proxy info is coming from a file.

        Return:
            A list of QNetworkProxy objects in order of preference.
        """
        if from_file:
            string_flags = QUrl.PrettyDecoded
        else:
            string_flags = QUrl.RemoveUserInfo
            if query.url().scheme() == 'https':
                string_flags |= QUrl.RemovePath | QUrl.RemoveQuery

        result = self._resolver.call([query.url().toString(string_flags),
                                      query.peerHostName()])
        result_str = result.toString()
        if not result.isString():
            err = "Got strange value from FindProxyForURL: '{}'"
            raise EvalProxyError(err.format(result_str))
        return self._parse_proxy_string(result_str)
Exemplo n.º 4
0
class JsAlgorithm(QgsProcessingFeatureBasedAlgorithm):  # pylint: disable=too-many-public-methods
    """
    Javascript Algorithm
    """

    def __init__(self, description_file, script=None):
        super().__init__()

        self.script = script
        self.js_script = ''
        self.codec = None
        self.engine = None
        self.process_js_function = None
        self.json_exporter = None
        self.fields = None
        self._name = ''
        self._display_name = ''
        self._group = ''
        self.description_file = os.path.realpath(description_file) if description_file else None
        self.error = None
        self.commands = list()
        self.is_user_script = False
        if description_file:
            self.is_user_script = not description_file.startswith(JsUtils.builtin_scripts_folder())

        if self.script is not None:
            self.load_from_string()
        if self.description_file is not None:
            self.load_from_file()

    def createInstance(self):
        """
        Returns a new instance of this algorithm
        """
        if self.description_file is not None:
            return JsAlgorithm(self.description_file)

        return JsAlgorithm(description_file=None, script=self.script)

    def icon(self):
        """
        Returns the algorithm's icon
        """
        return GuiUtils.get_icon("providerJS.svg")

    def svgIconPath(self):
        """
        Returns a path to the algorithm's icon as a SVG file
        """
        return GuiUtils.get_icon_svg("providerJS.svg")

    def name(self):
        """
        Internal unique id for algorithm
        """
        return self._name

    def displayName(self):
        """
        User friendly display name
        """
        return self._display_name

    def shortDescription(self):
        """
        Returns the path to the script file, for use in toolbox tooltips
        """
        return self.description_file

    def group(self):
        """
        Returns the algorithm's group
        """
        return self._group

    def groupId(self):
        """
        Returns the algorithm's group ID
        """
        return self._group

    def load_from_string(self):
        """
        Load the algorithm from a string
        """
        lines = self.script.split('\n')
        self._name = 'unnamedalgorithm'
        self._display_name = self.tr('[Unnamed algorithm]')
        self.parse_script(iter(lines))

    def load_from_file(self):
        """
        Load the algorithm from a file
        """
        filename = os.path.basename(self.description_file)
        self._display_name = self._name
        self._name = filename[:filename.rfind('.')]
        self._display_name = self._name.replace('_', ' ')
        with open(self.description_file, 'r') as f:
            lines = [line.strip() for line in f]
        self.parse_script(iter(lines))

    def parse_script(self, lines):
        """
        Parse the lines from an JS script, initializing parameters and outputs as encountered
        """
        self.script = ''
        js_script_lines = list()
        self.error = None
        ender = 0
        line = next(lines).strip('\n').strip('\r')
        while ender < 10:
            if line.startswith('//#'):
                try:
                    self.process_metadata_line(line)
                except Exception:  # pylint: disable=broad-except
                    self.error = self.tr('This script has a syntax error.\n'
                                         'Problem with line: {0}').format(line)
            else:
                if line == '':
                    ender += 1
                else:
                    ender = 0
                js_script_lines.append(line)
            self.script += line + '\n'
            try:
                line = next(lines).strip('\n').strip('\r')
            except StopIteration:
                break
        self.js_script = '\n'.join(js_script_lines)

    def process_metadata_line(self, line):
        """
        Processes a "metadata" (##) line
        """
        line = line.replace('//#', '')

        # special commands
        #if line.lower().strip().startswith('dontuserasterpackage'):
        #    self.use_raster_package = False
        #    return

        value, type_ = self.split_tokens(line)
        if type_.lower().strip() == 'group':
            self._group = value
            return
        if type_.lower().strip() == 'name':
            self._name = self._display_name = value
            self._name = JsUtils.strip_special_characters(self._name.lower())
            return

        self.process_parameter_line(line)

    @staticmethod
    def split_tokens(line):
        """
        Attempts to split a line into tokens
        """
        tokens = line.split('=')
        return tokens[0], tokens[1]

    def process_parameter_line(self, line):
        """
        Processes a single script line representing a parameter
        """
        value, _ = self.split_tokens(line)
        description = JsUtils.create_descriptive_name(value)

        output = create_output_from_string(line)
        if output is not None:
            output.setName(value)
            output.setDescription(description)
            if issubclass(output.__class__, QgsProcessingOutputDefinition):
                self.addOutput(output)
            else:
                # destination type parameter
                self.addParameter(output)
        else:
            line = JsUtils.upgrade_parameter_line(line)
            param = getParameterFromString(line)
            if param is not None:
                self.addParameter(param)
            else:
                self.error = self.tr('This script has a syntax error.\n'
                                     'Problem with line: {0}').format(line)

    def outputFields(self, fields):
        self.fields = fields
        return self.fields

    def outputCrs(self, inputCrs):
        self.input_crs = inputCrs
        return QgsCoordinateReferenceSystem('EPSG:4326')

    def prepareAlgorithm(self, parameters, context, feedback):
        """
        Prepares the algorithm
        """
        self.engine = QJSEngine()
        js_feedback = self.engine.newQObject(feedback)
        QQmlEngine.setObjectOwnership(feedback, QQmlEngine.CppOwnership)
        self.engine.globalObject().setProperty("feedback", js_feedback)
        js = """
        function process(feature)
        {
          res = func(JSON.parse(feature))
          if ( res && res.stack && res.message )
            return res;
         return JSON.stringify(res);
        }
        """

        for param in self.parameterDefinitions():
            if param.isDestination():
                continue

            if param.name() not in parameters or parameters[param.name()] is None:
                js += '{}=None;\n'.format(param.name())
                continue

            if isinstance(param,
                            (QgsProcessingParameterField, QgsProcessingParameterString, QgsProcessingParameterFile)):
                value = self.parameterAsString(parameters, param.name(), context)
                js += '{}="{}";'.format(param.name(), value)
            elif isinstance(param, QgsProcessingParameterNumber):
                value = self.parameterAsDouble(parameters, param.name(), context)
                js += '{}={};'.format(param.name(), value)

        js += self.js_script
        result = self.engine.evaluate(js)

        user_func = self.engine.globalObject().property("func")
        if not user_func:
            raise QgsProcessingException('No \'func\' function detected in script')
        if not user_func.isCallable():
            raise QgsProcessingException('Object \'func\' is not a callable function')

        self.process_js_function = self.engine.globalObject().property("process")
        self.json_exporter = QgsJsonExporter()

        self.codec = QTextCodec.codecForName("System")

        return True

    def outputName(self):
        return 'Processed'

    def processFeature(self, feature, context, feedback):
        """
        Executes the algorithm
        """
        self.json_exporter.setSourceCrs(self.input_crs)
        geojson = self.json_exporter.exportFeature(feature)
        res = self.process_js_function.call([geojson])
        if not res:
            return []
        if res.isError():
            error = "Uncaught exception at line {}:{}".format(res.property("lineNumber").toInt(),
                                                            res.toString())
            raise QgsProcessingException(error)

        return QgsJsonUtils.stringToFeatureList(res.toString(), self.fields, self.codec)

    def shortHelpString(self):
        """
        Returns the algorithms helper string
        """
        if self.description_file is None:
            return ''

        help_file = self.description_file + '.help'
        print(help_file)
        if os.path.exists(help_file):
            with open(help_file) as f:
                descriptions = json.load(f)

            return QgsProcessingUtils.formatHelpMapAsHtml(descriptions, self)

        return ''

    def tr(self, string, context=''):
        """
        Translates a string
        """
        if context == '':
            context = 'JsAlgorithmProvider'
        return QCoreApplication.translate(context, string)