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)
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)
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
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)
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)