Example #1
0
def sendVerificationMail(db, user, email_id=None):
    cursor = db.cursor()

    if email_id is None:
        cursor.execute("""SELECT email
                            FROM users
                           WHERE id=%s""",
                       (user.id,))

        email_id, = cursor.fetchone()

    cursor.execute("""SELECT email, verification_token
                        FROM useremails
                       WHERE id=%s""",
                   (email_id,))

    email, verification_token = cursor.fetchone()

    if verification_token is None:
        verification_token = auth.getToken(encode=base64.b16encode)

        with db.updating_cursor("useremails") as cursor:
            cursor.execute("""UPDATE useremails
                                 SET verification_token=%s
                               WHERE id=%s""",
                           (verification_token, email_id))

    if configuration.base.ACCESS_SCHEME == "http":
        protocol = "http"
    else:
        protocol = "https"

    administrators = dbutils.getAdministratorContacts(db, indent=2)

    if administrators:
        administrators = ":\n\n%s" % administrators
    else:
        administrators = "."

    recipients = [mailutils.User(user.name, email, user.fullname)]
    subject = "[Critic] Please verify your email: %s" % email
    body = textutils.reflow("""
This is a message from the Critic code review system at %(hostname)s.  The user
'%(username)s' on this system has added this email address to his/her account.
If this is you, please confirm this by following this link:

  %(url_prefix)s/verifyemail?email=%(email)s&token=%(verification_token)s

If this is not you, you can safely ignore this email.  If you wish to report
abuse, please contact the Critic system's administrators%(administrators)s
""" % { "hostname": configuration.base.HOSTNAME,
        "username": user.name,
        "email": email,
        "url_prefix": "%s://%s" % (protocol, configuration.base.HOSTNAME),
        "verification_token": verification_token,
        "administrators": administrators })

    mailutils.sendMessage(recipients, subject, body)
Example #2
0
def sendVerificationMail(db, user, email_id=None):
    cursor = db.cursor()

    if email_id is None:
        cursor.execute("""SELECT email
                            FROM users
                           WHERE id=%s""",
                       (user.id,))

        email_id, = cursor.fetchone()

    cursor.execute("""SELECT email, verification_token
                        FROM useremails
                       WHERE id=%s""",
                   (email_id,))

    email, verification_token = cursor.fetchone()

    if verification_token is None:
        verification_token = auth.getToken(encode=base64.b16encode)

        cursor.execute("""UPDATE useremails
                             SET verification_token=%s
                           WHERE id=%s""",
                       (verification_token, email_id))

    if configuration.base.ACCESS_SCHEME == "http":
        protocol = "http"
    else:
        protocol = "https"

    administrators = dbutils.getAdministratorContacts(db, indent=2)

    if administrators:
        administrators = ":\n\n%s" % administrators
    else:
        administrators = "."

    recipients = [mailutils.User(user.name, email, user.fullname)]
    subject = "[Critic] Please verify your email: %s" % email
    body = textutils.reflow("""
This is a message from the Critic code review system at %(hostname)s.  The user
'%(username)s' on this system has added this email address to his/her account.
If this is you, please confirm this by following this link:

  %(url_prefix)s/verifyemail?email=%(email)s&token=%(verification_token)s

If this is not you, you can safely ignore this email.  If you wish to report
abuse, please contact the Critic system's administrators%(administrators)s
""" % { "hostname": configuration.base.HOSTNAME,
        "username": user.name,
        "email": email,
        "url_prefix": "%s://%s" % (protocol, configuration.base.HOSTNAME),
        "verification_token": verification_token,
        "administrators": administrators })

    mailutils.sendMessage(recipients, subject, body)
Example #3
0
    def update(self, trackedbranch_id, repository_id, local_name, remote, remote_name, forced):
        repository = gitutils.Repository.fromId(self.db, repository_id)

        try:
            with repository.relaycopy("branchtracker") as relay:
                relay.run("remote", "add", "source", remote)

                current = None
                new = None
                tags = []

                if local_name == "*":
                    output = relay.run("fetch", "source", "refs/tags/*:refs/tags/*", include_stderr=True)
                    for line in output.splitlines():
                        if "[new tag]" in line:
                            tags.append(line.rsplit(" ", 1)[-1])
                else:
                    relay.run("fetch", "--quiet", "--no-tags", "source", "refs/heads/%s:refs/remotes/source/%s" % (remote_name, remote_name))
                    try:
                        current = repository.revparse("refs/heads/%s" % local_name)
                    except gitutils.GitReferenceError:
                        # It's okay if the local branch doesn't exist (yet).
                        pass
                    new = relay.run("rev-parse", "refs/remotes/source/%s" % remote_name).strip()

                if current != new or tags:
                    if local_name == "*":
                        refspecs = [("refs/tags/%s" % tag) for tag in tags]
                    else:
                        refspecs = ["refs/remotes/source/%s:refs/heads/%s"
                                    % (remote_name, local_name)]

                    returncode, stdout, stderr = relay.run(
                        "push", "--force", "origin", *refspecs,
                        env={ "CRITIC_FLAGS": "trackedbranch_id=%d" % trackedbranch_id,
                              "TERM": "dumb" },
                        check_errors=False)

                    stderr_lines = []
                    remote_lines = []

                    for line in stderr.splitlines():
                        if line.endswith(DUMB_SUFFIX):
                            line = line[:-len(DUMB_SUFFIX)]
                        stderr_lines.append(line)
                        if line.startswith("remote: "):
                            line = line[8:]
                            remote_lines.append(line)

                    if returncode == 0:
                        if local_name == "*":
                            for tag in tags:
                                self.info("  updated tag: %s" % tag)
                        elif current:
                            self.info("  updated branch: %s: %s..%s" % (local_name, current[:8], new[:8]))
                        else:
                            self.info("  created branch: %s: %s" % (local_name, new[:8]))

                        hook_output = ""

                        for line in remote_lines:
                            self.debug("  [hook] " + line)
                            hook_output += line + "\n"

                        if local_name != "*":
                            cursor = self.db.cursor()
                            cursor.execute("INSERT INTO trackedbranchlog (branch, from_sha1, to_sha1, hook_output, successful) VALUES (%s, %s, %s, %s, %s)",
                                           (trackedbranch_id, current if current else '0' * 40, new if new else '0' * 40, hook_output, True))
                            self.db.commit()
                    else:
                        if local_name == "*":
                            error = "update of tags from %s failed" % remote
                        else:
                            error = "update of branch %s from %s in %s failed" % (local_name, remote_name, remote)

                        hook_output = ""

                        for line in stderr_lines:
                            error += "\n    " + line

                        for line in remote_lines:
                            hook_output += line + "\n"

                        self.error(error)

                        cursor = self.db.cursor()

                        if local_name != "*":
                            cursor.execute("""INSERT INTO trackedbranchlog (branch, from_sha1, to_sha1, hook_output, successful)
                                                   VALUES (%s, %s, %s, %s, %s)""",
                                           (trackedbranch_id, current, new, hook_output, False))
                            self.db.commit()

                        cursor.execute("SELECT uid FROM trackedbranchusers WHERE branch=%s", (trackedbranch_id,))
                        recipients = [dbutils.User.fromId(self.db, user_id) for (user_id,) in cursor]

                        if local_name == "*":
                            mailutils.sendMessage(recipients, "%s: update of tags from %s stopped!" % (repository.name, remote),
                                                  """\
The automatic update of tags in
  %s:%s
from the remote
  %s
failed, and has been disabled.  Manual intervention is required to resume the
automatic updating.

Output from Critic's git hook
-----------------------------

%s""" % (configuration.base.HOSTNAME, repository.path, remote, hook_output))
                        else:
                            mailutils.sendMessage(recipients, "%s: update from %s in %s stopped!" % (local_name, remote_name, remote),
                                                  """\
The automatic update of the branch '%s' in
  %s:%s
from the branch '%s' in
  %s
failed, and has been disabled.  Manual intervention is required to resume the
automatic updating.

Output from Critic's git hook
-----------------------------

%s""" % (local_name, configuration.base.HOSTNAME, repository.path, remote_name, remote, hook_output))

                        # Disable the tracking.
                        return False
                else:
                    self.debug("  fetched %s in %s; no changes" % (remote_name, remote))

            # Everything went well; keep the tracking enabled.
            return True
        except:
            exception = traceback.format_exc()

            if local_name == "*":
                error = "  update of tags from %s failed" % remote
            else:
                error = "  update of branch %s from %s in %s failed" % (local_name, remote_name, remote)

            for line in exception.splitlines():
                error += "\n    " + line

            self.error(error)

            # The expected failure (in case of diverged branches, or review branch
            # irregularities) is a failed "git push" and is handled above.  This is
            # an unexpected failure, so might be intermittent.  Leave the tracking
            # enabled and spam the system administrator(s).
            return True
Example #4
0
def processFilterHookEvent(db, event_id, logfn):
    cursor = db.cursor()

    cursor.execute(
        """SELECT filters.extension, filters.uid, filters.path,
                             filters.name, events.review, events.uid, events.data
                        FROM extensionfilterhookevents AS events
                        JOIN extensionhookfilters AS filters ON (filters.id=events.filter)
                       WHERE events.id=%s""", (event_id, ))

    # Note:
    # - filter_user_id / filter_user represent the user whose filter was
    #   triggered.
    # - user_id /user represent the user that added commits and thereby
    #   triggered the filter.

    (extension_id, filter_user_id, filter_path, filterhook_name, review_id,
     user_id, filter_data) = cursor.fetchone()

    extension = Extension.fromId(db, extension_id)
    filter_user = dbutils.User.fromId(db, filter_user_id)

    installed_sha1, _ = extension.getInstalledVersion(db, filter_user)

    if installed_sha1 is False:
        # Invalid event (user doesn't have extension installed); do nothing.
        # The event will be deleted by the caller.
        return

    manifest = extension.getManifest(sha1=installed_sha1)

    for role in manifest.roles:
        if isinstance(role, FilterHookRole) and role.name == filterhook_name:
            break
    else:
        # Invalid event (installed version of extension doesn't have the named
        # filter hook role); do nothing.  The event will be deleted by the
        # caller.
        return

    cursor.execute(
        """SELECT commit
                        FROM extensionfilterhookcommits
                       WHERE event=%s""", (event_id, ))
    commit_ids = [commit_id for (commit_id, ) in cursor]

    cursor.execute(
        """SELECT file
                        FROM extensionfilterhookfiles
                       WHERE event=%s""", (event_id, ))
    file_ids = [file_id for (file_id, ) in cursor]

    argv = """

(function () {
   var review = new critic.Review(%(review_id)d);
   var user = new critic.User(%(user_id)d);
   var repository = review.repository;
   var commits = new critic.CommitSet(
     %(commit_ids)r.map(
       function (commit_id) {
         return repository.getCommit(commit_id);
       }));
   var files = %(file_ids)r.map(
     function (file_id) {
       return critic.File.find(file_id);
     });
   return [%(filter_data)s, review, user, commits, files];
 })()

""" % {
        "filter_data": htmlutils.jsify(filter_data),
        "review_id": review_id,
        "user_id": user_id,
        "commit_ids": commit_ids,
        "file_ids": file_ids
    }

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

    logfn("argv=%r" % argv)
    logfn("script=%r" % role.script)
    logfn("function=%r" % role.function)

    try:
        executeProcess(manifest, "filterhook", role.script, role.function,
                       extension_id, filter_user_id, argv,
                       configuration.extensions.LONG_TIMEOUT)
    except (ProcessTimeout, ProcessError) as error:
        review = dbutils.Review.fromId(db, review_id)

        recipients = set([filter_user])

        author = extension.getAuthor(db)
        if author is None:
            recipients.update(dbutils.User.withRole(db, "administrator"))
        else:
            recipients.add(author)

        body = """\
An error occurred while processing an extension hook filter event!

Filter details:

  Extension:   %(extension.title)s
  Filter hook: %(role.title)s
  Repository:  %(repository.name)s
  Path:        %(filter.path)s
  Data:        %(filter.data)s

Event details:

  Review:  r/%(review.id)d "%(review.summary)s"
  Commits: %(commits)s

Error details:

  Error:  %(error.message)s
  Output:%(error.output)s

-- critic"""

        commits = (gitutils.Commit.fromId(db, review.repository, commit_id)
                   for commit_id in commit_ids)
        commits_text = "\n           ".join(
            ('%s "%s"' % (commit.sha1[:8], commit.niceSummary())
             for commit in commits))

        if isinstance(error, ProcessTimeout):
            error_output = " N/A"
        else:
            error_output = "\n\n    " + "\n    ".join(
                error.stderr.splitlines())

        body = body % {
            "extension.title": extension.getTitle(db),
            "role.title": role.title,
            "repository.name": review.repository.name,
            "filter.path": filter_path,
            "filter.data": htmlutils.jsify(filter_data),
            "review.id": review.id,
            "review.summary": review.summary,
            "commits": commits_text,
            "error.message": error.message,
            "error.output": error_output
        }

        mailutils.sendMessage(recipients=list(recipients),
                              subject="Failed: " + role.title,
                              body=body)
Example #5
0
def processFilterHookEvent(db, event_id, logfn):
    cursor = db.cursor()

    cursor.execute("""SELECT filters.extension, filters.uid, filters.path,
                             filters.name, events.review, events.uid, events.data
                        FROM extensionfilterhookevents AS events
                        JOIN extensionhookfilters AS filters ON (filters.id=events.filter)
                       WHERE events.id=%s""",
                   (event_id,))

    # Note:
    # - filter_user_id / filter_user represent the user whose filter was
    #   triggered.
    # - user_id /user represent the user that added commits and thereby
    #   triggered the filter.

    (extension_id, filter_user_id, filter_path,
     filterhook_name, review_id, user_id, filter_data) = cursor.fetchone()

    extension = Extension.fromId(db, extension_id)
    filter_user = dbutils.User.fromId(db, filter_user_id)

    installed_sha1, _ = extension.getInstalledVersion(db, filter_user)

    if installed_sha1 is False:
        # Invalid event (user doesn't have extension installed); do nothing.
        # The event will be deleted by the caller.
        return

    manifest = extension.getManifest(sha1=installed_sha1)

    for role in manifest.roles:
        if isinstance(role, FilterHookRole) and role.name == filterhook_name:
            break
    else:
        # Invalid event (installed version of extension doesn't have the named
        # filter hook role); do nothing.  The event will be deleted by the
        # caller.
        return

    cursor.execute("""SELECT commit
                        FROM extensionfilterhookcommits
                       WHERE event=%s""",
                   (event_id,))
    commit_ids = [commit_id for (commit_id,) in cursor]

    cursor.execute("""SELECT file
                        FROM extensionfilterhookfiles
                       WHERE event=%s""",
                   (event_id,))
    file_ids = [file_id for (file_id,) in cursor]

    argv = """

(function () {
   var review = new critic.Review(%(review_id)d);
   var user = new critic.User(%(user_id)d);
   var repository = review.repository;
   var commits = new critic.CommitSet(
     %(commit_ids)r.map(
       function (commit_id) {
         return repository.getCommit(commit_id);
       }));
   var files = %(file_ids)r.map(
     function (file_id) {
       return critic.File.find(file_id);
     });
   return [%(filter_data)s, review, user, commits, files];
 })()

""" % { "filter_data": htmlutils.jsify(filter_data),
        "review_id": review_id,
        "user_id": user_id,
        "commit_ids": commit_ids,
        "file_ids": file_ids }

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

    logfn("argv=%r" % argv)
    logfn("script=%r" % role.script)
    logfn("function=%r" % role.function)

    try:
        executeProcess(
            manifest, "filterhook", role.script, role.function, extension_id,
            filter_user_id, argv, configuration.extensions.LONG_TIMEOUT)
    except (ProcessTimeout, ProcessError) as error:
        review = dbutils.Review.fromId(db, review_id)

        recipients = set([filter_user])

        author = extension.getAuthor(db)
        if author is None:
            recipients.update(dbutils.User.withRole(db, "administrator"))
        else:
            recipients.add(author)

        body = """\
An error occurred while processing an extension hook filter event!

Filter details:

  Extension:   %(extension.title)s
  Filter hook: %(role.title)s
  Repository:  %(repository.name)s
  Path:        %(filter.path)s
  Data:        %(filter.data)s

Event details:

  Review:  r/%(review.id)d "%(review.summary)s"
  Commits: %(commits)s

Error details:

  Error:  %(error.message)s
  Output:%(error.output)s

-- critic"""

        commits = (gitutils.Commit.fromId(db, review.repository, commit_id)
                   for commit_id in commit_ids)
        commits_text = "\n           ".join(
            ('%s "%s"' % (commit.sha1[:8], commit.niceSummary())
             for commit in commits))

        if isinstance(error, ProcessTimeout):
            error_output = " N/A"
        else:
            error_output = "\n\n    " + "\n    ".join(error.stderr.splitlines())

        body = body % { "extension.title": extension.getTitle(db),
                        "role.title": role.title,
                        "repository.name": review.repository.name,
                        "filter.path": filter_path,
                        "filter.data": htmlutils.jsify(filter_data),
                        "review.id": review.id,
                        "review.summary": review.summary,
                        "commits": commits_text,
                        "error.message": error.message,
                        "error.output": error_output }

        mailutils.sendMessage(
            recipients=list(recipients),
            subject="Failed: " + role.title,
            body=body)