Ejemplo n.º 1
0
def listFilterHooks(db, user):
    cursor = db.cursor()

    installs = Extension.getInstalls(db, user)
    filterhooks = []

    for extension_id, version_id, version_sha1, is_universal in installs:
        if version_id is not None:
            cursor.execute(
                """SELECT 1
                                FROM extensionroles
                                JOIN extensionfilterhookroles ON (role=id)
                               WHERE version=%s""", (version_id, ))

            if not cursor.fetchone():
                continue

            extension = Extension.fromId(db, extension_id)
            manifest = extension.getManifest(sha1=version_sha1)
        else:
            try:
                extension = Extension.fromId(db, extension_id)
            except ExtensionError:
                # If the author/hosting user no longer exists, or the extension
                # directory no longer exists or is inaccessible, ignore the
                # extension.
                continue

            try:
                manifest = Manifest.load(extension.getPath())
            except ManifestError:
                # If the MANIFEST is missing or invalid, we can't know whether
                # the extension has any filter hook roles, so assume it doesn't
                # and ignore it.
                continue

            if not any(
                    isinstance(role, FilterHookRole)
                    for role in manifest.roles):
                continue

        filterhooks.append((extension, manifest,
                            sorted((role for role in manifest.roles
                                    if isinstance(role, FilterHookRole)),
                                   key=lambda role: role.title)))

    return sorted(filterhooks,
                  key=lambda (extension, manifest, roles): extension.getKey())
Ejemplo n.º 2
0
def listFilterHooks(db, user):
    cursor = db.cursor()

    installs = Extension.getInstalls(db, user)
    filterhooks = []

    for extension_id, version_id, version_sha1, is_universal in installs:
        if version_id is not None:
            cursor.execute("""SELECT 1
                                FROM extensionroles
                                JOIN extensionfilterhookroles ON (role=id)
                               WHERE version=%s""",
                           (version_id,))

            if not cursor.fetchone():
                continue

            extension = Extension.fromId(db, extension_id)
            manifest = extension.getManifest(sha1=version_sha1)
        else:
            try:
                extension = Extension.fromId(db, extension_id)
            except ExtensionError:
                # If the author/hosting user no longer exists, or the extension
                # directory no longer exists or is inaccessible, ignore the
                # extension.
                continue

            try:
                manifest = Manifest.load(extension.getPath())
            except ManifestError:
                # If the MANIFEST is missing or invalid, we can't know whether
                # the extension has any filter hook roles, so assume it doesn't
                # and ignore it.
                continue

            if not any(isinstance(role, FilterHookRole)
                       for role in manifest.roles):
                continue

        filterhooks.append((extension, manifest, sorted(
                    (role for role in manifest.roles
                     if isinstance(role, FilterHookRole)),
                    key=lambda role: role.title)))

    return sorted(filterhooks,
                  key=lambda (extension, manifest, roles): extension.getKey())
Ejemplo n.º 3
0
Archivo: page.py Proyecto: Aessy/critic
def execute(db, req, user):
    cursor = db.cursor()

    installs = Extension.getInstalls(db, user)

    argv = None
    stdin_data = None

    for extension_id, version_id, version_sha1, is_universal in installs:
        handlers = []

        if version_id is not None:
            cursor.execute("""SELECT script, function, path
                                FROM extensionroles
                                JOIN extensionpageroles ON (role=id)
                               WHERE version=%s
                            ORDER BY id ASC""",
                           (version_id,))

            for script, function, path_regexp in cursor:
                if re.match(path_regexp, req.path):
                    handlers.append((script, function))

            if not handlers:
                continue

            extension_path = getExtensionInstallPath(version_sha1)
            manifest = Manifest.load(extension_path)
        else:
            try:
                extension = Extension.fromId(db, extension_id)
            except ExtensionError:
                # If the author/hosting user no longer exists, or the extension
                # directory no longer exists or is inaccessible, ignore the
                # extension.
                continue

            try:
                manifest = Manifest.load(extension.getPath())
            except ManifestError:
                # If the MANIFEST is missing or invalid, we can't know whether
                # the extension has a page role handling the path, so assume it
                # doesn't and ignore it.
                continue

            for role in manifest.roles:
                if isinstance(role, PageRole) and re.match(role.regexp, req.path):
                    handlers.append((role.script, role.function))

            if not handlers:
                continue

        if argv is None:
            def param(raw):
                parts = raw.split("=", 1)
                if len(parts) == 1:
                    return "%s: null" % jsify(decodeURIComponent(raw))
                else:
                    return "%s: %s" % (jsify(decodeURIComponent(parts[0])),
                                       jsify(decodeURIComponent(parts[1])))

            if req.query:
                query = ("Object.freeze({ raw: %s, params: Object.freeze({ %s }) })"
                         % (jsify(req.query),
                            ", ".join(map(param, req.query.split("&")))))
            else:
                query = "null"

            headers = ("Object.freeze({ %s })"
                       % ", ".join(("%s: %s" % (jsify(name), jsify(value)))
                                   for name, value in req.getRequestHeaders().items()))

            argv = ("[%(method)s, %(path)s, %(query)s, %(headers)s]"
                    % { 'method': jsify(req.method),
                        'path': jsify(req.path),
                        'query': query,
                        'headers': headers })

        if req.method == "POST":
            if stdin_data is None:
                stdin_data = req.read()

        for script, function in handlers:
            before = time.time()

            try:
                stdout_data = executeProcess(
                    manifest, "page", script, function, extension_id, user.id,
                    argv, configuration.extensions.LONG_TIMEOUT, stdin=stdin_data)
            except ProcessTimeout:
                req.setStatus(500, "Extension Timeout")
                return "Extension timed out!"
            except ProcessError as error:
                req.setStatus(500, "Extension Failure")
                if error.returncode < 0:
                    return ("Extension failure: terminated by signal %d\n"
                            % -error.returncode)
                else:
                    return ("Extension failure: returned %d\n%s"
                            % (error.returncode, error.stderr))

            after = time.time()

            status = None
            headers = {}

            if not stdout_data:
                return False

            while True:
                try: line, stdout_data = stdout_data.split("\n", 1)
                except:
                    req.setStatus(500, "Extension Error")
                    return "Extension error: output format error.\n%r\n" % stdout_data

                if status is None:
                    try: status = int(line.strip())
                    except:
                        req.setStatus(500, "Extension Error")
                        return "Extension error: first line should contain only a numeric HTTP status code.\n%r\n" % line
                elif not line:
                    break
                else:
                    try: name, value = line.split(":", 1)
                    except:
                        req.setStatus(500, "Extension Error")
                        return "Extension error: header line should be on 'name: value' format.\n%r\n" % line
                    headers[name.strip()] = value.strip()

            if status is None:
                req.setStatus(500, "Extension Error")
                return "Extension error: first line should contain only a numeric HTTP status code.\n"

            content_type = "text/plain"

            for name, value in headers.items():
                if name.lower() == "content-type":
                    content_type = value
                    del headers[name]
                else:
                    headers[name] = value

            req.setStatus(status)
            req.setContentType(content_type)

            for name, value in headers.items():
                req.addResponseHeader(name, value)

            if content_type == "text/tutorial":
                req.setContentType("text/html")
                return renderTutorial(db, user, stdout_data)

            if content_type.startswith("text/html"):
                stdout_data += "\n\n<!-- extension execution time: %.2f seconds -->\n" % (after - before)

            return stdout_data

    return False
Ejemplo n.º 4
0
def doInstallExtension(db, user, extension, version):
    is_universal = user is None
    extension_id = extension.getExtensionID(db, create=True)
    manifest = extension.getManifest(version)

    # Detect conflicting extension installs.
    current_installs = Extension.getInstalls(db, user)
    for current_extension_id, _, _, current_is_universal in current_installs:
        # Two installs never conflict if one is universal and one is not.
        if is_universal != current_is_universal:
            continue

        try:
            current_extension = Extension.fromId(db, current_extension_id)
        except ExtensionError as error:
            # Invalid extension => no conflict.
            #
            # But if there would be a conflict, should the installed extension
            # later become valid again, then delete the installation.
            if extension.getName() == error.extension.getName():
                doUninstallExtension(db, user, error.extension)
            continue

        # Same extension => conflict
        #
        # The web UI will typically not let you try to do this; if the extension
        # is already installed the UI will only let you uninstall or upgrade it.
        # But you never know.  Also, there's a UNIQUE constraint in the database
        # that would prevent this, but with a significantly worse error message,
        # of course.
        if extension_id == current_extension_id:
            raise InstallationError(
                title="Conflicting install",
                message=("The extension <code>%s</code> is already "
                         "%sinstalled." %
                         (current_extension.getTitle(db),
                          "universally " if is_universal else "")),
                is_html=True)

        # Different extensions, same name => also conflict
        #
        # Two extensions with the same name are probably simply two forks of the
        # same extension, and are very likely to have overlapping and
        # conflicting functionality.  Also, extension resource paths only
        # contain the extension name as an identifier, and thus will conflict
        # between the two extensions, even if they are actually completely
        # unrelated.
        if extension.getName() == current_extension.getName():
            raise InstallationError(
                title="Conflicting install",
                message=("The extension <code>%s</code> is already "
                         "%sinstalled, and conflicts with the extension "
                         "<code>%s</code> since they have the same name." %
                         (current_extension.getTitle(db), "universally "
                          if is_universal else "", extension.getTitle(db))),
                is_html=True)

    cursor = db.cursor()

    if is_universal:
        user_id = None
    else:
        user_id = user.id

    if version is not None:
        sha1 = extension.prepareVersionSnapshot(version)

        cursor.execute(
            """SELECT id
                            FROM extensionversions
                           WHERE extension=%s
                             AND name=%s
                             AND sha1=%s""", (extension_id, version, sha1))
        row = cursor.fetchone()

        if row:
            (version_id, ) = row
        else:
            cursor.execute(
                """INSERT INTO extensionversions (extension, name, sha1)
                                   VALUES (%s, %s, %s)
                                RETURNING id""", (extension_id, version, sha1))
            (version_id, ) = cursor.fetchone()

            for role in manifest.roles:
                role.install(db, version_id)
    else:
        version_id = None

    cursor.execute(
        """INSERT INTO extensioninstalls (uid, extension, version)
                           VALUES (%s, %s, %s)""",
        (user_id, extension_id, version_id))
Ejemplo n.º 5
0
def execute(db, user, review, all_commits, old_head, new_head, output):
    cursor = db.cursor()

    installs = Extension.getInstalls(db, user)

    data = None

    for extension_id, version_id, version_sha1, is_universal in installs:
        handlers = []

        extension = Extension.fromId(db, extension_id)

        if version_id is not None:
            cursor.execute("""SELECT script, function
                                FROM extensionroles
                                JOIN extensionprocesscommitsroles ON (role=id)
                               WHERE version=%s
                            ORDER BY id ASC""",
                           (version_id,))

            handlers.extend(cursor)

            if not handlers:
                continue

            extension_path = getExtensionInstallPath(version_sha1)
            manifest = Manifest.load(extension_path)
        else:
            manifest = Manifest.load(extension.getPath())

            for role in manifest.roles:
                if isinstance(role, ProcessCommitsRole):
                    handlers.append((role.script, role.function))

            if not handlers:
                continue

        if data is None:
            commitset = log.commitset.CommitSet(all_commits)

            assert old_head is None or old_head in commitset.getTails()
            assert new_head in commitset.getHeads()
            assert len(commitset.getHeads()) == 1

            tails = commitset.getFilteredTails(review.repository)
            if len(tails) == 1:
                tail = gitutils.Commit.fromSHA1(db, review.repository, tails.pop())
                changeset_id = changeset.utils.createChangeset(
                    db, user, review.repository, from_commit=tail, to_commit=new_head)[0].id
                changeset_arg = "repository.getChangeset(%d)" % changeset_id
            else:
                changeset_arg = "null"

            commits_arg = "[%s]" % ",".join(
                [("repository.getCommit(%d)" % commit.getId(db))
                 for commit in all_commits])

            data = { "review_id": review.id,
                     "changeset": changeset_arg,
                     "commits": commits_arg }

        for script, function in handlers:
            class Error(Exception):
                pass

            def print_header():
                header = "%s::%s()" % (script, function)
                print >>output, ("\n[%s] %s\n[%s] %s"
                                 % (extension.getName(), header,
                                    extension.getName(), "=" * len(header)))

            try:
                argv = """

(function ()
 {
   var review = new critic.Review(%(review_id)d);
   var repository = review.repository;
   var changeset = %(changeset)s;
   var commitset = new critic.CommitSet(%(commits)s);

   return [review, changeset, commitset];
 })()

""" % data
                argv = re.sub("[ \n]+", " ", argv.strip())

                try:
                    stdout_data = executeProcess(
                        manifest, "processcommits", script, function, extension_id, user.id,
                        argv, configuration.extensions.SHORT_TIMEOUT)
                except ProcessTimeout:
                    raise Error("Timeout after %d seconds." % configuration.extensions.SHORT_TIMEOUT)
                except ProcessError as error:
                    if error.returncode < 0:
                        raise Error("Process terminated by signal %d." % -error.returncode)
                    else:
                        raise Error("Process returned %d.\n%s" % (error.returncode, error.stderr))

                if stdout_data.strip():
                    print_header()
                    for line in stdout_data.splitlines():
                        print >>output, "[%s] %s" % (extension.getName(), line)
            except Error as error:
                print_header()
                print >>output, "[%s] Extension error: %s" % (extension.getName(), error.message)
Ejemplo n.º 6
0
def execute(db, req, user):
    cursor = db.cursor()

    installs = Extension.getInstalls(db, user)

    argv = None
    stdin_data = None

    for extension_id, version_id, version_sha1, is_universal in installs:
        handlers = []

        if version_id is not None:
            cursor.execute(
                """SELECT script, function, path
                                FROM extensionroles
                                JOIN extensionpageroles ON (role=id)
                               WHERE version=%s
                            ORDER BY id ASC""", (version_id, ))

            for script, function, path_regexp in cursor:
                if re.match(path_regexp, req.path):
                    handlers.append((script, function))

            if not handlers:
                continue

            extension_path = getExtensionInstallPath(version_sha1)
            manifest = Manifest.load(extension_path)
        else:
            try:
                extension = Extension.fromId(db, extension_id)
            except ExtensionError:
                # If the author/hosting user no longer exists, or the extension
                # directory no longer exists or is inaccessible, ignore the
                # extension.
                continue

            try:
                manifest = Manifest.load(extension.getPath())
            except ManifestError:
                # If the MANIFEST is missing or invalid, we can't know whether
                # the extension has a page role handling the path, so assume it
                # doesn't and ignore it.
                continue

            for role in manifest.roles:
                if isinstance(role, PageRole) and re.match(
                        role.regexp, req.path):
                    handlers.append((role.script, role.function))

            if not handlers:
                continue

        if argv is None:

            def param(raw):
                parts = raw.split("=", 1)
                if len(parts) == 1:
                    return "%s: null" % jsify(decodeURIComponent(raw))
                else:
                    return "%s: %s" % (jsify(decodeURIComponent(
                        parts[0])), jsify(decodeURIComponent(parts[1])))

            if req.query:
                query = (
                    "Object.freeze({ raw: %s, params: Object.freeze({ %s }) })"
                    % (jsify(req.query), ", ".join(
                        map(param, req.query.split("&")))))
            else:
                query = "null"

            headers = ("Object.freeze({ %s })" % ", ".join(
                ("%s: %s" % (jsify(name), jsify(value)))
                for name, value in req.getRequestHeaders().items()))

            argv = ("[%(method)s, %(path)s, %(query)s, %(headers)s]" % {
                'method': jsify(req.method),
                'path': jsify(req.path),
                'query': query,
                'headers': headers
            })

        if req.method == "POST":
            if stdin_data is None:
                stdin_data = req.read()

        for script, function in handlers:
            before = time.time()

            try:
                stdout_data = executeProcess(
                    manifest,
                    "page",
                    script,
                    function,
                    extension_id,
                    user.id,
                    argv,
                    configuration.extensions.LONG_TIMEOUT,
                    stdin=stdin_data)
            except ProcessTimeout:
                req.setStatus(500, "Extension Timeout")
                return "Extension timed out!"
            except ProcessError as error:
                req.setStatus(500, "Extension Failure")
                if error.returncode < 0:
                    return ("Extension failure: terminated by signal %d\n" %
                            -error.returncode)
                else:
                    return ("Extension failure: returned %d\n%s" %
                            (error.returncode, error.stderr))

            after = time.time()

            status = None
            headers = {}

            if not stdout_data:
                return False

            while True:
                try:
                    line, stdout_data = stdout_data.split("\n", 1)
                except:
                    req.setStatus(500, "Extension Error")
                    return "Extension error: output format error.\n%r\n" % stdout_data

                if status is None:
                    try:
                        status = int(line.strip())
                    except:
                        req.setStatus(500, "Extension Error")
                        return "Extension error: first line should contain only a numeric HTTP status code.\n%r\n" % line
                elif not line:
                    break
                else:
                    try:
                        name, value = line.split(":", 1)
                    except:
                        req.setStatus(500, "Extension Error")
                        return "Extension error: header line should be on 'name: value' format.\n%r\n" % line
                    headers[name.strip()] = value.strip()

            if status is None:
                req.setStatus(500, "Extension Error")
                return "Extension error: first line should contain only a numeric HTTP status code.\n"

            content_type = "text/plain"

            for name, value in headers.items():
                if name.lower() == "content-type":
                    content_type = value
                    del headers[name]
                else:
                    headers[name] = value

            req.setStatus(status)
            req.setContentType(content_type)

            for name, value in headers.items():
                req.addResponseHeader(name, value)

            if content_type == "text/tutorial":
                req.setContentType("text/html")
                return renderTutorial(db, user, stdout_data)

            if content_type.startswith("text/html"):
                stdout_data += "\n\n<!-- extension execution time: %.2f seconds -->\n" % (
                    after - before)

            return stdout_data

    return False
Ejemplo n.º 7
0
def execute(db, req, user, document, links, injected, profiler=None):
    cursor = db.cursor()

    installs = Extension.getInstalls(db, user)

    def get_matching_path(path_regexp):
        if re.match(path_regexp, req.path):
            return (req.path, req.query)
        elif re.match(path_regexp, req.original_path):
            return (req.original_path, req.original_query)
        else:
            return None, None

    query = None

    for extension_id, version_id, version_sha1, is_universal in installs:
        handlers = []

        try:
            if version_id is not None:
                cursor.execute("""SELECT script, function, path
                                    FROM extensionroles
                                    JOIN extensioninjectroles ON (role=id)
                                   WHERE version=%s
                                ORDER BY id ASC""",
                               (version_id,))

                for script, function, path_regexp in cursor:
                    path, query = get_matching_path(path_regexp)
                    if path is not None:
                        handlers.append((path, query, script, function))

                if not handlers:
                    continue

                extension = Extension.fromId(db, extension_id)
                manifest = Manifest.load(getExtensionInstallPath(version_sha1))
            else:
                extension = Extension.fromId(db, extension_id)
                manifest = Manifest.load(extension.getPath())

                for role in manifest.roles:
                    if isinstance(role, InjectRole):
                        path, query = get_matching_path(role.regexp)
                        if path is not None:
                            handlers.append((path, query, role.script, role.function))

                if not handlers:
                    continue

            def construct_query(query):
                if not query:
                    return "null"

                params = urlparse.parse_qs(query, keep_blank_values=True)

                for key in params:
                    values = params[key]
                    if len(values) == 1:
                        if not values[0]:
                            params[key] = None
                        else:
                            params[key] = values[0]

                return ("Object.freeze({ raw: %s, params: Object.freeze(%s) })"
                        % (json_encode(query), json_encode(params)))

            preferences = None
            commands = []

            for path, query, script, function in handlers:
                argv = "[%s, %s]" % (jsify(path), construct_query(query))

                try:
                    stdout_data = executeProcess(
                        manifest, "inject", script, function, extension_id, user.id, argv,
                        configuration.extensions.SHORT_TIMEOUT)
                except ProcessTimeout:
                    raise InjectError("Timeout after %d seconds." % configuration.extensions.SHORT_TIMEOUT)
                except ProcessError as error:
                    if error.returncode < 0:
                        raise InjectError("Process terminated by signal %d." % -error.returncode)
                    else:
                        raise InjectError("Process returned %d.\n%s" % (error.returncode, error.stderr))

                for line in stdout_data.splitlines():
                    if line.strip():
                        commands.append(processLine(path, line.strip()))

            for command, value in commands:
                if command == "script":
                    document.addExternalScript(value, use_static=False, order=1)
                elif command == "stylesheet":
                    document.addExternalStylesheet(value, use_static=False, order=1)
                elif command == "link":
                    for index, (_, label, _, _) in enumerate(links):
                        if label == value["label"]:
                            if value["url"] is None:
                                del links[index]
                            else:
                                links[index][0] = value["url"]
                            break
                    else:
                        if value["url"] is not None:
                            links.append([value["url"], value["label"], None, None])
                elif command == "preference":
                    if not preferences:
                        preferences = []
                        injected.setdefault("preferences", []).append(
                            (extension.getName(), extension.getAuthor(db), preferences))
                    preferences.append(value)

            if profiler:
                profiler.check("inject: %s" % extension.getKey())
        except ExtensionError as error:
            document.comment("\n\n[%s] Extension error:\nInvalid extension:\n%s\n\n"
                             % (error.extension.getKey(), error.message))
        except ManifestError as error:
            document.comment("\n\n[%s] Extension error:\nInvalid MANIFEST:\n%s\n\n"
                             % (extension.getKey(), error.message))
        except InjectError as error:
            document.comment("\n\n[%s] Extension error:\n%s\n\n"
                             % (extension.getKey(), error.message))
Ejemplo n.º 8
0
def renderManageExtensions(req, db, user):
    if not configuration.extensions.ENABLED:
        administrators = dbutils.getAdministratorContacts(db, as_html=True)
        raise page.utils.DisplayMessage(
            title="Extension support not enabled",
            body=(("<p>This Critic system does not support extensions.</p>"
                   "<p>Contact %s to have it enabled, or see the "
                   "<a href='/tutorial?item=administration#extensions'>"
                   "section on extensions</a> in the system administration "
                   "tutorial for more information.</p>")
                  % administrators),
            html=True)

    cursor = db.cursor()

    what = req.getParameter("what", "available")
    selected_versions = page.utils.json_decode(req.getParameter("select", "{}"))
    focused = req.getParameter("focus", None)

    if what == "installed":
        title = "Installed Extensions"
        listed_extensions = []
        for extension_id, _, _, _ in Extension.getInstalls(db, user):
            try:
                listed_extensions.append(Extension.fromId(db, extension_id))
            except ExtensionError as error:
                listed_extensions.append(error)
    else:
        title = "Available Extensions"
        listed_extensions = Extension.find(db)

    req.content_type = "text/html; charset=utf-8"

    document = htmlutils.Document(req)
    document.setTitle("Manage Extensions")

    html = document.html()
    head = html.head()
    body = html.body()

    def generateRight(target):
        target.a("button", href="tutorial?item=extensions").text("Tutorial")
        target.text(" ")
        target.a("button", href="tutorial?item=extensions-api").text("API Documentation")

    page.utils.generateHeader(body, db, user, current_page="extensions", generate_right=generateRight)

    document.addExternalStylesheet("resource/manageextensions.css")
    document.addExternalScript("resource/manageextensions.js")
    document.addInternalScript(user.getJS())

    table = page.utils.PaleYellowTable(body, title)

    def addTitleRightLink(url, label):
        if user.name != req.user:
            url += "&user=%s" % user.name
        table.titleRight.text(" ")
        table.titleRight.a(href=url).text("[" + label + " extensions]")

    if what != "installed" or focused:
        addTitleRightLink("/manageextensions?what=installed", "installed")
    if what != "available" or focused:
        addTitleRightLink("/manageextensions?what=available", "available")

    for item in listed_extensions:
        if isinstance(item, ExtensionError):
            extension_error = item
            extension = item.extension
        else:
            extension_error = None
            extension = item

        if focused and extension.getKey() != focused:
            continue

        extension_path = extension.getPath()

        if extension.isSystemExtension():
            hosting_user = None
        else:
            hosting_user = extension.getAuthor(db)

        selected_version = selected_versions.get(extension.getKey(), False)
        installed_sha1, installed_version = extension.getInstalledVersion(db, user)
        universal_sha1, universal_version = extension.getInstalledVersion(db, None)
        installed_upgradeable = universal_upgradeable = False

        if extension_error is None:
            if installed_sha1:
                current_sha1 = extension.getCurrentSHA1(installed_version)
                installed_upgradeable = installed_sha1 != current_sha1
            if universal_sha1:
                current_sha1 = extension.getCurrentSHA1(universal_version)
                universal_upgradeable = universal_sha1 != current_sha1

        def massage_version(version):
            if version is None:
                return "live"
            elif version:
                return "version/%s" % version
            else:
                return None

        if selected_version is False:
            selected_version = installed_version
        if selected_version is False:
            selected_version = universal_version

        install_version = massage_version(selected_version)
        installed_version = massage_version(installed_version)
        universal_version = massage_version(universal_version)

        manifest = None

        if extension_error is None:
            try:
                if selected_version is False:
                    manifest = extension.getManifest()
                else:
                    manifest = extension.getManifest(selected_version)
            except ManifestError as error:
                pass
        elif installed_sha1:
            manifest = extension.getManifest(installed_version, installed_sha1)
        elif universal_sha1:
            manifest = extension.getManifest(universal_version, universal_sha1)

        if manifest:
            if what == "available" and manifest.hidden:
                # Hide from view unless the user is hosting the extension, or is
                # an administrator and the extension is a system extension.
                if extension.isSystemExtension():
                    if not user.hasRole(db, "administrator"):
                        continue
                elif hosting_user != user:
                    continue
        else:
            if hosting_user != user:
                continue

        extension_id = extension.getExtensionID(db, create=False)

        if not user.isAnonymous():
            buttons = []

            if extension_id is not None:
                cursor.execute("""SELECT 1
                                    FROM extensionstorage
                                   WHERE extension=%s
                                     AND uid=%s""",
                               (extension_id, user.id))

                if cursor.fetchone():
                    buttons.append(("Clear storage",
                                    ("clearExtensionStorage(%s, %s)"
                                     % (htmlutils.jsify(extension.getAuthorName()),
                                        htmlutils.jsify(extension.getName())))))

            if not installed_version:
                if manifest and install_version and install_version != universal_version:
                    buttons.append(("Install",
                                    ("installExtension(%s, %s, %s)"
                                     % (htmlutils.jsify(extension.getAuthorName()),
                                        htmlutils.jsify(extension.getName()),
                                        htmlutils.jsify(install_version)))))
            else:
                buttons.append(("Uninstall",
                                ("uninstallExtension(%s, %s)"
                                 % (htmlutils.jsify(extension.getAuthorName()),
                                    htmlutils.jsify(extension.getName())))))

                if manifest and (install_version != installed_version
                                 or (installed_sha1 and installed_upgradeable)):
                    if install_version == installed_version:
                        label = "Upgrade"
                    else:
                        label = "Install"

                    buttons.append(("Upgrade",
                                    ("reinstallExtension(%s, %s, %s)"
                                     % (htmlutils.jsify(extension.getAuthorName()),
                                        htmlutils.jsify(extension.getName()),
                                        htmlutils.jsify(install_version)))))

            if user.hasRole(db, "administrator"):
                if not universal_version:
                    if manifest and install_version:
                        buttons.append(("Install (universal)",
                                        ("installExtension(%s, %s, %s, true)"
                                         % (htmlutils.jsify(extension.getAuthorName()),
                                            htmlutils.jsify(extension.getName()),
                                            htmlutils.jsify(install_version)))))
                else:
                    buttons.append(("Uninstall (universal)",
                                    ("uninstallExtension(%s, %s, true)"
                                     % (htmlutils.jsify(extension.getAuthorName()),
                                        htmlutils.jsify(extension.getName())))))

                    if manifest and (install_version != universal_version
                                     or (universal_sha1 and universal_upgradeable)):
                        if install_version == universal_version:
                            label = "Upgrade (universal)"
                        else:
                            label = "Install (universal)"

                        buttons.append((label,
                                        ("reinstallExtension(%s, %s, %s, true)"
                                         % (htmlutils.jsify(extension.getAuthorName()),
                                            htmlutils.jsify(extension.getName()),
                                            htmlutils.jsify(universal_version)))))
        else:
            buttons = None

        def renderItem(target):
            target.span("name").innerHTML(extension.getTitle(db, html=True))

            if hosting_user:
                is_author = manifest and manifest.isAuthor(db, hosting_user)
                is_sole_author = is_author and len(manifest.authors) == 1
            else:
                is_sole_author = False

            if extension_error is None:
                span = target.span("details")
                span.b().text("Details: ")
                select = span.select("details", critic_author=extension.getAuthorName(), critic_extension=extension.getName())
                select.option(value='', selected="selected" if selected_version is False else None).text("Select version")
                versions = extension.getVersions()
                if versions:
                    optgroup = select.optgroup(label="Official Versions")
                    for version in versions:
                        optgroup.option(value="version/%s" % version, selected="selected" if selected_version == version else None).text("%s" % version.upper())
                optgroup = select.optgroup(label="Development")
                optgroup.option(value='live', selected="selected" if selected_version is None else None).text("LIVE")

            if manifest:
                is_installed = bool(installed_version)

                if is_installed:
                    target.span("installed").text(" [installed]")
                else:
                    is_installed = bool(universal_version)

                    if is_installed:
                        target.span("installed").text(" [installed (universal)]")

                target.div("description").preformatted().text(manifest.description, linkify=True)

                if not is_sole_author:
                    authors = target.div("authors")
                    authors.b().text("Author%s:" % ("s" if len(manifest.authors) > 1 else ""))
                    authors.text(", ".join(author.name for author in manifest.getAuthors()))
            else:
                is_installed = False

                div = target.div("description broken").preformatted()

                if extension_error is None:
                    anchor = div.a(href="loadmanifest?key=%s" % extension.getKey())
                    anchor.text("[This extension has an invalid MANIFEST file]")
                else:
                    div.text("[This extension has been deleted or has become inaccessible]")

            if selected_version is False:
                return

            pages = []
            injects = []
            processcommits = []
            filterhooks = []
            scheduled = []

            if manifest:
                for role in manifest.roles:
                    if isinstance(role, PageRole):
                        pages.append(role)
                    elif isinstance(role, InjectRole):
                        injects.append(role)
                    elif isinstance(role, ProcessCommitsRole):
                        processcommits.append(role)
                    elif isinstance(role, FilterHookRole):
                        filterhooks.append(role)
                    elif isinstance(role, ScheduledRole):
                        scheduled.append(role)

            role_table = target.table("roles")

            if pages:
                role_table.tr().th(colspan=2).text("Pages")

                for role in pages:
                    row = role_table.tr()
                    url = "%s/%s" % (dbutils.getURLPrefix(db, user), role.pattern)
                    if is_installed and "*" not in url:
                        row.td("pattern").a(href=url).text(url)
                    else:
                        row.td("pattern").text(url)
                    td = row.td("description")
                    td.text(role.description)

            if injects:
                role_table.tr().th(colspan=2).text("Page Injections")

                for role in injects:
                    row = role_table.tr()
                    row.td("pattern").text("%s/%s" % (dbutils.getURLPrefix(db, user), role.pattern))
                    td = row.td("description")
                    td.text(role.description)

            if processcommits:
                role_table.tr().th(colspan=2).text("ProcessCommits hooks")
                ul = role_table.tr().td(colspan=2).ul()

                for role in processcommits:
                    li = ul.li()
                    li.text(role.description)

            if filterhooks:
                role_table.tr().th(colspan=2).text("FilterHook hooks")

                for role in filterhooks:
                    row = role_table.tr()
                    row.td("title").text(role.title)
                    row.td("description").text(role.description)

            if scheduled:
                role_table.tr().th(colspan=2).text("Scheduled hooks")

                for role in scheduled:
                    row = role_table.tr()
                    row.td("pattern").text("%s @ %s" % (role.frequency, role.at))
                    td = row.td("description")
                    td.text(role.description)

        installed_by = ""

        if extension_id is not None:
            cursor.execute("""SELECT uid
                                FROM extensioninstalls
                                JOIN extensions ON (extensions.id=extensioninstalls.extension)
                               WHERE extensions.id=%s""",
                           (extension.getExtensionID(db, create=False),))

            user_ids = set(user_id for user_id, in cursor.fetchall())
            if user_ids:
                installed_by = " (installed"
                if None in user_ids:
                    installed_by += " universally"
                    user_ids.remove(None)
                    if user_ids:
                        installed_by += " and"
                if user_ids:
                    installed_by += (" by %d user%s"
                                  % (len(user_ids),
                                     "s" if len(user_ids) > 1 else ""))
                installed_by += ")"

        table.addItem("Extension", renderItem, extension_path + "/" + installed_by, buttons)

    document.addInternalScript("var selected_versions = %s;" % page.utils.json_encode(selected_versions))

    return document
Ejemplo n.º 9
0
def execute(db, user, review, all_commits, old_head, new_head, output):
    cursor = db.cursor()

    installs = Extension.getInstalls(db, user)

    data = None

    for extension_id, version_id, version_sha1, is_universal in installs:
        handlers = []

        extension = Extension.fromId(db, extension_id)

        if version_id is not None:
            cursor.execute(
                """SELECT script, function
                                FROM extensionroles
                                JOIN extensionprocesscommitsroles ON (role=id)
                               WHERE version=%s
                            ORDER BY id ASC""", (version_id, ))

            handlers.extend(cursor)

            if not handlers:
                continue

            extension_path = getExtensionInstallPath(version_sha1)
            manifest = Manifest.load(extension_path)
        else:
            manifest = Manifest.load(extension.getPath())

            for role in manifest.roles:
                if isinstance(role, ProcessCommitsRole):
                    handlers.append((role.script, role.function))

            if not handlers:
                continue

        if data is None:
            commitset = log.commitset.CommitSet(all_commits)

            assert old_head is None or old_head in commitset.getTails()
            assert new_head in commitset.getHeads()
            assert len(commitset.getHeads()) == 1

            tails = commitset.getFilteredTails(review.repository)
            if len(tails) == 1:
                tail = gitutils.Commit.fromSHA1(db, review.repository,
                                                tails.pop())
                changeset_id = changeset.utils.createChangeset(
                    db,
                    user,
                    review.repository,
                    from_commit=tail,
                    to_commit=new_head)[0].id
                changeset_arg = "repository.getChangeset(%d)" % changeset_id
            else:
                changeset_arg = "null"

            commits_arg = "[%s]" % ",".join(
                [("repository.getCommit(%d)" % commit.getId(db))
                 for commit in all_commits])

            data = {
                "review_id": review.id,
                "changeset": changeset_arg,
                "commits": commits_arg
            }

        for script, function in handlers:

            class Error(Exception):
                pass

            def print_header():
                header = "%s::%s()" % (script, function)
                print >> output, ("\n[%s] %s\n[%s] %s" %
                                  (extension.getName(), header,
                                   extension.getName(), "=" * len(header)))

            try:
                argv = """

(function ()
 {
   var review = new critic.Review(%(review_id)d);
   var repository = review.repository;
   var changeset = %(changeset)s;
   var commitset = new critic.CommitSet(%(commits)s);

   return [review, changeset, commitset];
 })()

""" % data
                argv = re.sub("[ \n]+", " ", argv.strip())

                try:
                    stdout_data = executeProcess(
                        manifest, "processcommits", script, function,
                        extension_id, user.id, argv,
                        configuration.extensions.SHORT_TIMEOUT)
                except ProcessTimeout:
                    raise Error("Timeout after %d seconds." %
                                configuration.extensions.SHORT_TIMEOUT)
                except ProcessError as error:
                    if error.returncode < 0:
                        raise Error("Process terminated by signal %d." %
                                    -error.returncode)
                    else:
                        raise Error("Process returned %d.\n%s" %
                                    (error.returncode, error.stderr))

                if stdout_data.strip():
                    print_header()
                    for line in stdout_data.splitlines():
                        print >> output, "[%s] %s" % (extension.getName(),
                                                      line)
            except Error as error:
                print_header()
                print >> output, "[%s] Extension error: %s" % (
                    extension.getName(), error.message)
Ejemplo n.º 10
0
def doInstallExtension(db, user, extension, version):
    auth.AccessControl.accessExtension(db, "install", extension)

    is_universal = user is None
    extension_id = extension.getExtensionID(db, create=True)
    manifest = extension.getManifest(version)

    # Detect conflicting extension installs.
    current_installs = Extension.getInstalls(db, user)
    for current_extension_id, _, _, current_is_universal in current_installs:
        # Two installs never conflict if one is universal and one is not.
        if is_universal != current_is_universal:
            continue

        try:
            current_extension = Extension.fromId(db, current_extension_id)
        except ExtensionError as error:
            # Invalid extension => no conflict.
            #
            # But if there would be a conflict, should the installed extension
            # later become valid again, then delete the installation.
            if extension.getName() == error.extension.getName():
                doUninstallExtension(db, user, error.extension)
            continue

        # Same extension => conflict
        #
        # The web UI will typically not let you try to do this; if the extension
        # is already installed the UI will only let you uninstall or upgrade it.
        # But you never know.  Also, there's a UNIQUE constraint in the database
        # that would prevent this, but with a significantly worse error message,
        # of course.
        if extension_id == current_extension_id:
            raise InstallationError(
                title="Conflicting install",
                message=("The extension <code>%s</code> is already "
                         "%sinstalled."
                         % (current_extension.getTitle(db),
                            "universally " if is_universal else "")),
                is_html=True)

        # Different extensions, same name => also conflict
        #
        # Two extensions with the same name are probably simply two forks of the
        # same extension, and are very likely to have overlapping and
        # conflicting functionality.  Also, extension resource paths only
        # contain the extension name as an identifier, and thus will conflict
        # between the two extensions, even if they are actually completely
        # unrelated.
        if extension.getName() == current_extension.getName():
            raise InstallationError(
                title="Conflicting install",
                message=("The extension <code>%s</code> is already "
                         "%sinstalled, and conflicts with the extension "
                         "<code>%s</code> since they have the same name."
                         % (current_extension.getTitle(db),
                            "universally " if is_universal else "",
                            extension.getTitle(db))),
                is_html=True)

    cursor = db.cursor()

    if is_universal:
        user_id = None
    else:
        user_id = user.id

    if version is not None:
        sha1 = extension.prepareVersionSnapshot(version)

        cursor.execute("""SELECT id
                            FROM extensionversions
                           WHERE extension=%s
                             AND name=%s
                             AND sha1=%s""",
                       (extension_id, version, sha1))
        row = cursor.fetchone()

        if row:
            (version_id,) = row
        else:
            cursor.execute("""INSERT INTO extensionversions (extension, name, sha1)
                                   VALUES (%s, %s, %s)
                                RETURNING id""",
                           (extension_id, version, sha1))
            (version_id,) = cursor.fetchone()

            for role in manifest.roles:
                role.install(db, version_id)
    else:
        version_id = None

    cursor.execute("""INSERT INTO extensioninstalls (uid, extension, version)
                           VALUES (%s, %s, %s)""",
                   (user_id, extension_id, version_id))
Ejemplo n.º 11
0
def execute(db, req, user, document, links, injected, profiler=None):
    cursor = db.cursor()

    installs = Extension.getInstalls(db, user)

    def get_matching_path(path_regexp):
        if re.match(path_regexp, req.path):
            return (req.path, req.query)
        elif re.match(path_regexp, req.original_path):
            return (req.original_path, req.original_query)
        else:
            return None, None

    query = None

    for extension_id, version_id, version_sha1, is_universal in installs:
        handlers = []

        try:
            if version_id is not None:
                cursor.execute(
                    """SELECT script, function, path
                                    FROM extensionroles
                                    JOIN extensioninjectroles ON (role=id)
                                   WHERE version=%s
                                ORDER BY id ASC""", (version_id, ))

                for script, function, path_regexp in cursor:
                    path, query = get_matching_path(path_regexp)
                    if path is not None:
                        handlers.append((path, query, script, function))

                if not handlers:
                    continue

                extension = Extension.fromId(db, extension_id)
                manifest = Manifest.load(getExtensionInstallPath(version_sha1))
            else:
                extension = Extension.fromId(db, extension_id)
                manifest = Manifest.load(extension.getPath())

                for role in manifest.roles:
                    if isinstance(role, InjectRole):
                        path, query = get_matching_path(role.regexp)
                        if path is not None:
                            handlers.append(
                                (path, query, role.script, role.function))

                if not handlers:
                    continue

            def construct_query(query):
                if not query:
                    return "null"

                params = urlparse.parse_qs(query, keep_blank_values=True)

                for key in params:
                    values = params[key]
                    if len(values) == 1:
                        if not values[0]:
                            params[key] = None
                        else:
                            params[key] = values[0]

                return (
                    "Object.freeze({ raw: %s, params: Object.freeze(%s) })" %
                    (json_encode(query), json_encode(params)))

            preferences = None
            commands = []

            for path, query, script, function in handlers:
                argv = "[%s, %s]" % (jsify(path), construct_query(query))

                try:
                    stdout_data = executeProcess(
                        manifest, "inject", script, function, extension_id,
                        user.id, argv, configuration.extensions.SHORT_TIMEOUT)
                except ProcessTimeout:
                    raise InjectError("Timeout after %d seconds." %
                                      configuration.extensions.SHORT_TIMEOUT)
                except ProcessError as error:
                    if error.returncode < 0:
                        raise InjectError("Process terminated by signal %d." %
                                          -error.returncode)
                    else:
                        raise InjectError("Process returned %d.\n%s" %
                                          (error.returncode, error.stderr))

                for line in stdout_data.splitlines():
                    if line.strip():
                        commands.append(processLine(path, line.strip()))

            for command, value in commands:
                if command == "script":
                    document.addExternalScript(value,
                                               use_static=False,
                                               order=1)
                elif command == "stylesheet":
                    document.addExternalStylesheet(value,
                                                   use_static=False,
                                                   order=1)
                elif command == "link":
                    for index, (_, label, _, _) in enumerate(links):
                        if label == value["label"]:
                            if value["url"] is None:
                                del links[index]
                            else:
                                links[index][0] = value["url"]
                            break
                    else:
                        if value["url"] is not None:
                            links.append(
                                [value["url"], value["label"], None, None])
                elif command == "preference":
                    if not preferences:
                        preferences = []
                        injected.setdefault("preferences", []).append(
                            (extension.getName(), extension.getAuthor(db),
                             preferences))
                    preferences.append(value)

            if profiler:
                profiler.check("inject: %s" % extension.getKey())
        except ExtensionError as error:
            document.comment(
                "\n\n[%s] Extension error:\nInvalid extension:\n%s\n\n" %
                (error.extension.getKey(), error.message))
        except ManifestError as error:
            document.comment(
                "\n\n[%s] Extension error:\nInvalid MANIFEST:\n%s\n\n" %
                (extension.getKey(), error.message))
        except InjectError as error:
            document.comment("\n\n[%s] Extension error:\n%s\n\n" %
                             (extension.getKey(), error.message))