text:
# frozen_string_literal: true

class SearchController < ApplicationController
  def index
    # This might be ugly, but it's better than the alternative.
    #
    # And it's kinda clever.

    title, title_operation,
    body, body_operation,
    why, why_operation,
    username, username_operation = %i[title body why username].map do |s|
      SearchHelper.parse_search_params(params, s, current_user)
    end.flatten

    if [title_operation, body_operation, why_operation, username_operation].any?(&:!)
      render json: { error: 'Unauthenticated users cannot use regex search' }, status: 403
      return
    end

    user_reputation = params[:user_reputation].to_i || 0

    case params[:feedback]
    when /true/
      feedback = :is_tp
    when /false/
      feedback = :is_fp
    when /NAA/
      feedback = :is_naa
    end

    @results = if params[:reason].present?
                 Reason.find(params[:reason]).posts.includes_for_post_row
               else
                 Post.all.includes_for_post_row
               end

    per_page = user_signed_in? && params[:per_page].present? ? [params[:per_page].to_i, 10_000].min : 100

    search_string = []
    search_params = {}
    [[:username, username, username_operation], [:title, title, title_operation],
     [:why, why, why_operation]].each do |si|
      if si[1].present? && si[1] != '%%'
        search_string << "IFNULL(`posts`.`#{si[0]}`, '') #{si[2]} :#{si[0]}"
        search_params[si[0]] = si[1]
      end
    end

    if body.present?
      if ['LIKE', 'NOT LIKE'].include?(body_operation) && params[:body_is_like] != '1'
        # If the operation would be LIKE, hijack it and use our fulltext index for a search instead.
        # UNLESS... params[:body_is_like] is set, in which case the user has explicitly specified a LIKE query.
        @results = @results.match_search(body, with_search_score: false, posts: :body)
      else
        if params[:body_is_like] == '1' && !user_signed_in?
          flash[:warning] = 'Unregistered users cannot use LIKE searches on the body field. Please sign in.'
          redirect_to(search_path) && return
        end
        # Otherwise, it's REGEX or NOT REGEX, which fulltext won't do - fall back on search_string and params
        search_string << "IFNULL(`posts`.`body`, '') #{body_operation} :body"
        search_params[:body] = body.present? ? body : '%%'
      end
    end

    @results = @results.where(search_string.join(params[:or_search].present? ? ' OR ' : ' AND '), **search_params)
    @results = @results.includes(:reasons).includes(:feedbacks) if params[:option].nil?

    if params[:has_no_feedback] == '1'
      @results = @results.joins('LEFT JOIN feedbacks fbcounter ON fbcounter.post_id = posts.id').where('fbcounter.id is null')
    end

    if feedback.present?
      @results = @results.where(feedback => true)
    elsif params[:feedback] == 'conflicted'
      @results = @results.where(is_tp: true, is_fp: true)
    end

    @results = case params[:user_rep_direction]
               when '>='
                 if user_reputation > 0
                   @results.where('IFNULL(user_reputation, 0) >= :rep', rep: user_reputation)
                 end
               when '=='
                 @results.where('IFNULL(user_reputation, 0) = :rep', rep: user_reputation)
               when '<='
                 @results.where('IFNULL(user_reputation, 0) <= :rep', rep: user_reputation)
               else
                 @results
               end

    @results = @results.where(site_id: params[:site]) if params[:site].present?

    @results = @results.where('revision_count > 1') if params[:edited].present?

    @results = @results.includes(feedbacks: [:user])

    case params[:autoflagged].try(:downcase)
    when 'yes'
      @results = @results.autoflagged
    when 'no'
      @results = @results.not_autoflagged
    end

    post_type = case params[:post_type].try(:downcase).try(:[], 0)
                when 'q'
                  'questions'
                when 'a'
                  'a'
                end

    if post_type.present?
      unmatched = @results.where.not("link LIKE '%/questions/%' OR link LIKE '%/a/%'")
      @results =  if params[:post_type_include_unmatched]
                    @results.where('link like ?', "%/#{post_type}/%").or(unmatched)
                  else
                    @results.where('link like ?', "%/#{post_type}/%")
                  end
    end

    respond_to do |format|
      format.html do
        @counts_by_accuracy_group = @results.group(:is_tp, :is_fp, :is_naa).count
        @counts_by_feedback = %i[is_tp is_fp is_naa].each_with_index.map do |symbol, i|
          [symbol, @counts_by_accuracy_group.select { |k, _v| k[i] }.values.sum]
        end.to_h
        @total_count = @counts_by_accuracy_group.values.sum

        @results = case params[:feedback_filter]
                   when 'tp'
                     @results.where(is_tp: true)
                             .paginate(page: params[:page], per_page: per_page,
                                       total_entries: @counts_by_feedback[:is_tp])
                   when 'fp'
                     @results.where(is_fp: true)
                             .paginate(page: params[:page], per_page: per_page,
                                       total_entries: @counts_by_feedback[:is_fp])
                   when 'naa'
                     @results.where(is_naa: true)
                             .paginate(page: params[:page], per_page: per_page,
                                       total_entries: @counts_by_feedback[:is_naa])
                   else
                     @results.paginate(page: params[:page], per_page: per_page,
                                       total_entries: @total_count)
                   end.order(Arel.sql('`posts`.`created_at` DESC'))

        render :search
      end
      format.json do
        render json: @results
      end
      format.rss { render :search, layout: false }
      format.xml { render 'search.rss', layout: false }
    end
  end
end


text:
# frozen_string_literal: true

class RedisLogController < ApplicationController
  before_action :authenticate_user!
  before_action :verify_developer

  def save
    redis = redis(logger: true)
    new_expire = REDIS_LOG_EXPIRATION * 1000
    timestamp = params[:timestamp]
    request_id = params[:request_id]
    redis.multi do
      redis.sadd('saved_requests', "#{timestamp}/#{request_id}")
      redis.expire("request/#{timestamp}/#{request_id}", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/request_headers", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/response_headers", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/params", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/exception", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/logs", new_expire)
    end
    redirect_to redis_log_request_path(timestamp: timestamp, request_id: request_id)
  end

  def unsave
    redis = redis(logger: true)
    new_expire = REDIS_LOG_EXPIRATION
    timestamp = params[:timestamp]
    request_id = params[:request_id]
    redis.multi do
      redis.srem('saved_requests', "#{timestamp}/#{request_id}")
      redis.expire("request/#{timestamp}/#{request_id}", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/request_headers", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/response_headers", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/params", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/exception", new_expire)
      redis.expire("request/#{timestamp}/#{request_id}/logs", new_expire)
    end
    redirect_to redis_log_request_path(timestamp: timestamp, request_id: request_id)
  end

  def show
    @request = get_request params[:timestamp].to_f, params[:request_id]
  end

  def by_user
    redis = redis(logger: true)
    user_id = params[:id]
    si, ei = page
    @sessions = redis.zrevrange("user_sessions/#{user_id}", si, ei).to_a.map do |session_id|
      requests = redis.zrange("session/#{session_id}/requests", 0, 20, with_scores: true)
      if redis.zcard("session/#{session_id}/requests") == 0
        redis.multi do
          redis.del "session/#{session_id}/requests"
          redis.del "session/#{session_id}"
        end
        {}
      else
        redis.hgetall("session/#{session_id}").merge(
          id: session_id,
          user_id: user_id,
          count: requests.length,
          requests: requests.map do |request_id, timestamp|
                      r = get_request(timestamp, request_id)
                      redis.zrem "session/#{session_id}/requests", request_id if r.empty?
                      r
                    end.reject(&:empty?)
        )
      end
    end.reject(&:empty?)
    render :user_sessions
  end

  def index
    redis = redis(logger: true)
    si, ei = page
    @requests = redis.zrevrange('requests', si, ei, with_scores: true).map do |request_id, timestamp|
      r = get_request(timestamp, request_id)
      redis.zrem 'requests', request_id if r.empty?
      r
    end.reject(&:empty?)
  end

  def by_session
    redis = redis(logger: true)
    @session_id = params[:id]
    si, ei = page
    @requests = if redis.zcard("session/#{@session_id}/requests") == 0
                  redis.multi do
                    redis.del "session/#{@session_id}/requests"
                    redis.del "session/#{@session_id}"
                  end
                  {}
                else
                  redis.zrange("session/#{@session_id}/requests", si, ei, with_scores: true).map do |request_id, timestamp|
                    r = get_request(timestamp, request_id)
                    redis.zrem "session/#{@session_id}/requests", request_id if r.empty?
                    r
                  end.reject(&:empty?)
                end
  end

  def by_status
    redis = redis(logger: true)
    @status = params[:status]
    si, ei = page
    @requests = redis.zrevrange("requests/status/#{@status}", si, ei, with_scores: true).to_a.map do |request_id, timestamp|
      r = get_request(timestamp, request_id)
      redis.zrem "requests/status/#{@status}", request_id if r.empty?
      r
    end.reject(&:empty?)
  end

  def by_path
    redis = redis(logger: true)
    @method = params[:method]
    @path = params[:path]
    @format = params[:format]
    si, ei = page
    @rlog = "requests/by_path/#{@method}/#{@path}.#{@format}"
    request.format = :html
    @requests = redis.zrevrange("requests/by_path/#{@method}/#{@path}.#{@format}", si, ei, with_scores: true).to_a.map do |request_id, timestamp|
      r = get_request(timestamp, request_id)
      redis.zrem "requests/by_path/#{@method}/#{@path}.#{@format}", request_id if r.empty?
      r
    end.reject(&:empty?)
  end

  private

  def get_request(timestamp, request_id)
    redis = redis(logger: true)
    from_redis = redis.hgetall("request/#{timestamp}/#{request_id}")
    return {} if from_redis.empty?
    from_redis['status'] ||= 'UNK'
    from_redis.merge(
      request_headers: redis.hgetall("request/#{timestamp}/#{request_id}/request_headers"),
      response_headers: redis.hgetall("request/#{timestamp}/#{request_id}/response_headers"),
      params: redis.hgetall("request/#{timestamp}/#{request_id}/params"),
      exception: redis.hgetall("request/#{timestamp}/#{request_id}/exception"),
      timestamp: timestamp,
      request_id: request_id,
      key: "#{timestamp.to_s.tr('.', '-')}-#{request_id}",
      logs: redis.lrange("request/#{timestamp}/#{request_id}/logs", 0, -1)
    )
  end

  def page
    pagesize = params[:pagesize].present? ? params[:pagesize].to_i : 50
    start_idx = (((params[:page].present? ? params[:page].to_i : 1) - 1) * pagesize)
    end_idx = start_idx + pagesize
    [start_idx, end_idx]
  end
end

repo_name: Charcoal-SE/metasmoke

repo_language: Ruby

mime_type: text/plain


text:
# frozen_string_literal: true

class ChannelsController < ApplicationController
  before_action :authenticate_user!, except: [:receive_email]
  before_action :verify_access, except: [:receive_email]
  skip_before_action :verify_authenticity_token, only: [:receive_email]

  # rubocop:disable Style/AccessorMethodName
  def get_email_address
    if current_user.channels_user.present?
      @secret = current_user.channels_user.secret
    elsif current_user.roles.count == 0
      flash[:danger] = 'You are not permitted to sign up to Channels.'
      redirect_to root_path
    else
      @secret = nil
      used = ChannelsUser.all.map(&:secret)
      while @secret.nil?
        attempt = SecureRandom.hex 18
        @secret = attempt unless used.include? attempt
      end

      ChannelsUser.create user: current_user, secret: @secret
    end
  end
  # rubocop:enable Style/AccessorMethodName

  def show_link
    if current_user.channels_user.present?
      @channels_user = current_user.channels_user
    else
      flash[:danger] = "You haven't signed up for Channels yet."
      redirect_to channels_email_path
    end
  end

  def receive_email
    message = JSON.parse(JSON.parse(request.raw_post)['Message'])

    user = message['mail']['destination'][0].split('@')[0]
    text = Base64.decode64(message['content']).gsub("=\r\n", '').gsub('3D', '')
    link = text.scan(%r{(https:\/\/stackoverflow.com\/c\/charcoal\/join\/confirmation\?token=[a-z0-9-]{36})})[0][0]

    ChannelsUser.find_by(secret: user).update(link: link)

    render plain: 'hi SNS!'
  end

  def verify_access
    return if user_signed_in? && (current_user.has_role?(:reviewer) || current_user.has_role?(:core) ||
      current_user.has_role?(:blacklist_manager) || current_user.has_role?(:smoke_detector_runner) ||
      current_user.has_role?(:admin) || current_user.has_role?(:developer))
    not_found
  end
end


text:
# frozen_string_literal: true

class FlagConditionsController < ApplicationController
  before_action :authenticate_user!
  before_action :verify_admin, only: %i[full_list user_overview validate_user]
  before_action :set_condition, only: %i[edit update destroy enable]
  before_action :verify_authorized, only: %i[edit update destroy enable]
  before_action :check_registration_status, only: [:new]
  before_action :set_preview_data, only: %i[new edit preview]
  before_action :verify_reviewer, only: [:sandbox]

  def index
    @conditions = current_user.flag_conditions
  end

  def sandbox
    @condition = FlagCondition.new(site_ids: Site.mains.map(&:id))
  end

  def full_list
    @conditions = FlagCondition.all.includes(:user)
    render :index
  end

  def new
    @condition = FlagCondition.new(site_ids: Site.mains.map(&:id),
                                   min_weight: 280,
                                   max_poster_rep: 1,
                                   min_reason_count: 1)
  end

  def create
    @condition = FlagCondition.new condition_params
    @condition.user = current_user
    @condition.sites = params[:flag_condition][:sites].map { |i| Site.find i.to_i if i.present? }.compact

    if @condition.save
      ValidateFlagConditionForUserJob.perform_later current_user.id

      flash[:success] = 'Created a new flagging condition.'
      redirect_to url_for(controller: :flag_conditions, action: :index)
    else
      render :new
    end
  end

  # PATCH /flagging/conditions/:id/toggle
  def enable
    @condition.flags_enabled = !@condition.flags_enabled

    return unless @condition.save
    flash[:success] = "#{@condition.flags_enabled ? 'Enabled' : 'Disabled'} condition."
    redirect_to url_for(controller: :flag_conditions, action: :index)
  end

  def edit
    return if @condition.flags_enabled
    @condition.flags_enabled = true
    @condition.validate

    @validation_errors = @condition.errors.dup
    @condition.restore_attributes
    @condition.errors.clear
  end

  def update
    @condition.sites = params[:flag_condition][:sites].map { |i| Site.find i.to_i if i.present? }.compact
    if @condition.update(condition_params)
      ValidateFlagConditionForUserJob.perform_later current_user.id

      flash[:success] = 'Updated your flagging condition.'
      redirect_to url_for(controller: :flag_conditions, action: :index)
    else
      render :edit, status: 422
    end
  end

  def destroy
    if @condition.destroy
      flash[:success] = 'Removed your flagging condition.'
    else
      flash[:danger] = 'Failed to remove the condition - contact a developer to find out why.'
    end
    redirect_to url_for(controller: :flag_conditions, action: :index)
  end

  def preview
    @condition = FlagCondition.new(condition_params)
    set_preview_data

    if params[:filter].present? && !params[:filter].empty?
      if params[:filter] == 'fps'
        @posts = @posts.where('posts.is_fp = TRUE OR posts.is_naa = TRUE')
      elsif params[:filter] == 'tps'
        @posts = @posts.where(is_tp: true)
      end
      @sites = Site.where(id: @posts.map(&:site_id)).to_a
    end

    respond_to do |format|
      format.js
    end
  end

  def one_click_setup
    return if current_user.write_authenticated
    flash[:warning] = 'You need to be write-authenticated before you can set up flagging.'
    redirect_to(url_for(controller: :authentication, action: :status))
  end

  def run_ocs
    unless current_user.write_authenticated
      flash[:warning] = 'You need to be write-authenticated before you can set up flagging.'
      redirect_to(url_for(controller: :authentication, action: :status))
    end

    FlagCondition.create(user: current_user, sites: Site.mains, flags_enabled: true, min_weight: 280, max_poster_rep: 1, min_reason_count: 1)
    UserSiteSetting.create(user: current_user, sites: Site.mains, max_flags: 7)
    current_user.update(flags_enabled: true)

    flash[:info] = "The necessary settings for autoflagging have been created - please review them to make sure you're happy."
    redirect_to url_for(controller: :flag_settings, action: :dashboard)
  end

  def user_overview
    @user = User.find params[:user]
    @conditions = FlagCondition.where(user: @user)
    @preferences = UserSiteSetting.where(user: @user)
    @logs = FlagLog.where(user: @user)
  end

  def validate_user
    @user = User.find params[:user]
    Thread.new do
      FlagCondition.validate_for_user @user, current_user
    end
    flash[:info] = 'Validation launched in background.'
    redirect_back fallback_location: root_path
  end

  private

  def set_condition
    @condition = FlagCondition.find params[:id]
  end

  def condition_params
    params.require(:flag_condition).permit(:min_weight, :max_poster_rep, :min_reason_count, :sites, :flags_enabled)
  end

  def verify_authorized
    return if current_user.has_role?(:admin) || @condition.user == current_user
    raise ActionController::RoutingError, 'Not Found'
  end

  def check_registration_status
    return unless FlagSetting['registration_enabled'] == '0' && !current_user.flags_enabled
    raise ActionController::RoutingError, 'Not Found'
  end

  def set_preview_data
    return unless @condition
    site_ids = if params['flag_condition']
                 params['flag_condition']['sites'].map(&:to_i)
               else
                 @condition.site_ids
               end

    posts = Post.joins(:reasons)
                .group(Arel.sql('posts.id'))
                .where('posts.user_reputation <= ?', @condition.max_poster_rep)
                .where(site_id: site_ids)
                .having('count(reasons.id) >= ?', @condition.min_reason_count)
                .having('sum(reasons.weight) >= ?', @condition.min_weight)

    post_feedback_results = posts.pluck(:is_tp, :is_fp, :is_naa)
    @false_positive_count = post_feedback_results.count do |rec|
      rec[1] || rec[2]
    end
    @true_positive_count = post_feedback_results.count do |rec|
      rec[0]
    end

    @posts = posts.includes(feedbacks: [:user]).order(Arel.sql('posts.id DESC')).paginate(page: params[:page], per_page: 100)
    @sites = Site.where(id: @posts.map(&:site_id)).to_a
  end
end


text:
# frozen_string_literal: true

require 'open-uri'
include APIHelper

class GithubController < ApplicationController
  skip_before_action :redis_log_request
  skip_before_action :verify_authenticity_token
  before_action :verify_github, except: %i[add_pullapprove_comment]
  before_action :check_if_smokedetector, only: [:add_pullapprove_comment]

  # The goal with most of these routes is to respond to things happening on the various GitHub repositories.
  # Each repository is set up with one or more WebHooks to the functions defined here.
  # Most of these recognize some specific thing happening in the repository and send a message to SmokeDetector,
  # which is then forwarded to SE chat.
  #
  # From a conceptual standpoint, the things we're looking to detect are:
  #  CI testing failures, and what failed
  #  CI testing success, if everything passed, but not success of individual sub-portions.
  #  Changes to either metasmoke's wiki or SmokeDetector's wiki, in order to start a new build of the Charcoal site.
  #  Creation of PRs
  #  Pushes to SmokeDetector's master branch, so we can update the deploy branch
  #  Pushes to metasmoke's master branch to update the developer info cache
  #
  # CI testing
  #   Unfortunately, keeping track of what's happening with CI testing is a bit complicated. To a significant
  #   extent, this is because there are multiple WebHooks which are fired, depending on the type of CI testing.
  #   "Statuses" WebHooks: CI testing performed by an external service (e.g. CircleCI and Travis CI).
  #     What is reported is entirely dependent on what is provided to GitHub from the external service through
  #     the GitHub API. Generally, this is status messages indicating starting and completing the testing.
  #     At least CircleCI updates status for each separate run within the suite of tests defined to be run.
  #     There are individual events which separately indicate failure, but overall success is indicated only by
  #     all expected runs resulting in "success".
  #   "Check suites" WebHooks: Fired upon completion of a suite of GitHub Actions. Indicates overall success/failure,
  #     but doesn't appear to indicate what failed. A PR is only indicated in the WebHook when the PR is created.
  #   "Check runs" WebHooks: Fired for each sub-task within a set of GitHub Actions, with an event when each run is
  #     "created" and/or "completed". The ones fired for "completed" indicate success or failure for each run.
  #     If a matrix of testing is defined, then each value in the matrix is considered a separate "run".
  #     A PR is only indicated in the WebHook when the PR is created.
  #
  #   GitHub Actions
  #     Failure of each separate run can be recognized and reported based on "failures" seen in "Check runs" WebHooks.
  #     Success of the overall suite can be recognized and reported based on "success" seen in "Check suites" WebHooks.
  #     If a push is done at the same time as a PR is created (e.g. editing a file on GitHub from which a PR is
  #       created), then two Check suites are created and run, or, at least, double the number of "Check runs" are
  #       created and executed, but we're only going to want to report success or failure once within a short time
  #       on a single commit.
  #
  #   CircleCI
  #     Failure of each separate run can be recognized and reported based on "failures" seen in "Statuses" WebHooks.
  #     Success of the overall suite can only be determined by seeing "success" in the status for all runs which
  #       are expected for a particular commit. There is no information provided as to the total number of runs
  #       which should be created per CI, so that's something we need to know a priori.
  #
  # Notes:
  #   GitHub sends an "action" parameter in some WebHooks. Its value is unavailable, due to "action" being a reserved
  #     parameter.

  # This is invoked by the route: /github/status_hook
  # GitHub fires the status WebHook when there's a status update reported to GitHub via the GitHub API.
  # It fires when an external CI service updates status (e.g. starts/finishes).
  # It is not fired for GitHub Actions. GitHub does fire this WebHook for GitHub Pages builds.
  # It is set up for the SmokeDetector repository to receive status changes.
  def status_hook
    # We're not interested in PR statuses or branches other than deploy
    unless params[:branches].index { |b| b[:name] == 'deploy' }
      render(plain: 'Not a commit on deploy. Uninterested.') && return
    end

    # Create a new CommitStatus

    if CommitStatus.find_by(commit_sha: params[:sha])
      render plain: 'Already recorded status for commit', status: 200
      return
    end

    if params[:state] == 'pending'
      render plain: "We don't record pending statuses", status: 200
      return
    end

    commit_sha = params[:sha]
    status = params[:state]
    commit_message = params[:commit][:commit][:message]
    ci_url = params[:target_url]

    ActionCable.server.broadcast 'smokedetector_messages', commit_status: {
      status: status,
      ci_url: ci_url,
      commit_sha: commit_sha,
      commit_message: commit_message
    }
    CommitStatus.create(commit_sha: commit_sha, status: status)

    render plain: 'OK', status: 200
  end

  # Fires when a wiki page is updated on Charcoal-SE/metasmoke or Charcoal-SE/SmokeDetector
  def gollum_hook
    # This only fires when we want to update the charcoal-se.org website, so we just unconditionally
    #   kick off a build of the charcoal-se.org website.
    APIHelper.authorized_post(
      'https://api.github.com/repos/Charcoal-SE/charcoal-se.github.io/actions/workflows/build.yml/dispatches',
      data: { 'ref' => 'site' }
    )
  end

  # Fires whenever a PR is opened on the SmokeDetetor repository to check for auto-blacklist and post stats
  def pull_request_hook
    unless request.request_parameters[:action] == 'opened'
      render(plain: 'Not a newly-opened PR. Uninterested.') && return
    end

    pull_request = params[:pull_request]

    SmokeDetector.send_message_to_charcoal("[PR##{pull_request[:number]}]"\
                                           "(https://github.com/Charcoal-SE/SmokeDetector/pull/#{pull_request[:number]})"\
                                           " (\"#{pull_request[:title]}\") opened by #{pull_request[:user][:login]}")

    unless pull_request[:user][:login] == 'SmokeDetector'
      render(plain: 'Not from SmokeDetector. Uninterested.') && return
    end

    text = pull_request[:body]

    response_text = ''

    # Identify blacklist type and use appropriate search

    domains = text.scan(/<!-- METASMOKE-BLACKLIST-WEBSITE (.*?) -->/)

    domains.each do |domain|
      domain = domain[0]

      num_tps = Post.where('body REGEXP ?', domain).where(is_tp: true).count
      num_fps = Post.where('body REGEXP ?', domain).where(is_fp: true).count
      num_naa = Post.where('body REGEXP ?', domain).where(is_naa: true).count

      response_text += get_line domain, num_tps, num_fps, num_naa
    end

    keywords = text.scan(/<!-- METASMOKE-BLACKLIST-KEYWORD (.*?) -->/)

    keywords.each do |keyword|
      keyword = keyword[0]

      num_tps = Post.where('body REGEXP ?', keyword).where(is_tp: true).count
      num_fps = Post.where('body REGEXP ?', keyword).where(is_fp: true).count
      num_naa = Post.where('body REGEXP ?', keyword).where(is_naa: true).count

      response_text += get_line keyword, num_tps, num_fps, num_naa
    end

    usernames = text.scan(/<!-- METASMOKE-BLACKLIST-USERNAME (.*?) -->/)

    usernames.each do |username|
      username = username[0]

      num_tps = Post.where('username REGEXP ?', username).where(is_tp: true).count
      num_fps = Post.where('username REGEXP ?', username).where(is_fp: true).count
      num_naa = Post.where('username REGEXP ?', username).where(is_naa: true).count

      response_text += get_line username, num_tps, num_fps, num_naa
    end

    watches = text.scan(/<!-- METASMOKE-BLACKLIST-WATCH_KEYWORD (.*?) -->/)

    watches.each do |watch|
      watch = watch[0]

      num_tps = Post.where('body REGEXP ?', watch).where(is_tp: true).count
      num_fps = Post.where('body REGEXP ?', watch).where(is_fp: true).count
      num_naa = Post.where('body REGEXP ?', watch).where(is_naa: true).count

      response_text += get_line watch, num_tps, num_fps, num_naa
    end

    Octokit.add_comment 'Charcoal-SE/SmokeDetector', pull_request[:number], response_text

    render plain: response_text, status: 200
  end

  def get_line(thing, num_tps, num_fps, num_naa)
    response_text  = "`#{thing}` has been seen in #{num_tps} true #{'positive'.pluralize(num_tps)}"
    response_text += ", #{num_fps} false #{'positive'.pluralize(num_fps)}"
    response_text += ", and #{num_naa} #{'NAA'.pluralize(num_naa)}."
    response_text + "\n\n"
  end

  # Fires when a PR is posted for our fake CI service to require reviews on
  def ci_hook
    case request.headers['HTTP_X_GITHUB_EVENT']
    when 'pull_request'
      data = JSON.parse(request.raw_post)
      pull_request = data['pull_request']
      case data['action']
      when 'opened', 'synchronize'
        commits = JSON.parse(Net::HTTP.get_response(URI.parse(pull_request['commits_url'])).body)
        commits.each do |commit|
          APIHelper.authorized_post(
            "https://api.github.com/repos/Charcoal-SE/SmokeDetector/statuses/#{commit['sha']}",
            state: 'pending',
            description: 'An Approve review is required before pull requests can be merged.',
            context: 'metasmoke/ci'
          )
        end
        render plain: "#{commits.length} commits set to pending."
      else
        render(plain: 'Not a newly-opened or updated PR; not interested.') && return
      end
    when 'pull_request_review'
      data = JSON.parse(request.raw_post)
      pull_request = data['pull_request']
      review = data['review']
      if data['action'] == 'submitted' && review['state'] == 'approved'
        commits = JSON.parse(Net::HTTP.get_response(URI.parse(pull_request['commits_url'])).body)
        commits.each do |commit|
          APIHelper.authorized_post(
            "https://api.github.com/repos/Charcoal-SE/SmokeDetector/statuses/#{commit['sha']}",
            state: 'success',
            description: 'PR approved :)',
            context: 'metasmoke/ci'
          )
        end

        render plain: "#{commits.length} commits approved."
      else
        render(plain: 'Not a submitted Approve review; not interested.') && return
      end
    else
      render(plain: "Pretty sure we don't subscribe to that event.") && return
    end
  end

  # Fires when a new push is made to Charcoal-SE/metasmoke, so we can bust
  # the status/code caches
  def metasmoke_push_hook
    Rails.cache.delete_matched %r{code_status/.*##{CurrentCommit}}
  end

  # Fires whenever anything is pushed, so we can automatically update `deploy`
  # to point to master's HEAD
  def update_deploy_to_master
    unless params[:ref] == 'refs/heads/master'
      render(plain: 'Not on master; not interested') && return
    end

    new_sha1 = params[:after]

    # false indicates a not-force-push
    Octokit.update_ref 'Charcoal-SE/SmokeDetector', 'heads/deploy', new_sha1, false

    # See https://developer.github.com/v3/activity/events/types/#webhook-payload-example-26
    # for what’s in `params`
    ActionCable.server.broadcast 'smokedetector_messages', deploy_updated: params
    ApiChannel.broadcast_to 'ref_update', event_type: 'update', event_class: 'Ref', object: params
  end

  # This is invoked by the route: /github/report_check_suite_success
  # It's intended to be triggered by GitHub Check Suites WebHooks in order to have SD report success of the full
  #   suite of GitHub Actions in chat. The hook is also fired by GitHub for failure, but we ignore those, as it's
  #   assumed those are being reported by the check_runs WebHook, due to check_suite event not having information
  #   as to which runs within a suite failed.
  # "check_suite" events are not triggered by external CI providers (e.g. CircleCI and Travis CI).
  # A check_suite event is fired for each set of CI testing which is run through GitHub Actions.
  # One "completed" event is sent for each GitHub Action complete set which is executed.
  # This route only reports successes and ignores suites started for SmokeDetector.
  # Concerns:
  #   When a PR is created directly from a branch at the same time as the branch, we can get called for two
  #   concurrent runs, which both show as associated with the PR, but we only want to report one for the commit.
  #   In that situation, we get two "completed" messages. This is resolved by using a Redis success counter to
  #   track how many we get and only forwarding the first one within 20 minutes to SmokeDetector.
  def report_check_suite_success
    data = params
    check_suite = data[:check_suite]
    conclusion = check_suite[:conclusion]
    branch = check_suite[:head_branch]
    pull_requests = check_suite[:pull_requests]
    pull_request = pull_requests[0]
    sha = check_suite[:head_sha]
    check_suite_status = check_suite[:status]
    repository = data[:repository]
    repo_name = repository[:name]
    repo_url = repository[:html_url]
    app_name = check_suite[:app][:name]
    sender_login = data[:sender][:login]
    pr_number = pull_request[:number] if pull_request.present?
    branch_path = ''
    branch_path = "/tree/#{branch}" if branch != 'master'

    # We are only interested in completed successes
    return if check_suite_status != 'completed' || conclusion != 'success' || sender_login == 'SmokeDetector'

    message = "[ [#{repo_name}](#{repo_url}) ]"
    message += " #{app_name}:"
    message += if pull_request.present?
                 " [#{conclusion}](#{repo_url}/pull/#{pr_number}/checks?sha=#{sha})"
               else
                 " [#{conclusion}](#{repo_url}/commit/#{sha}/checks)"
               end
    message += " on [#{sha.first(7)}](#{repo_url}/commit/#{sha.first(10)})"
    message += " by #{sender_login}" if sender_login.present?
    message += " in the [#{branch}](#{repo_url}#{branch_path}) branch" if branch.present?
    message += " for [PR ##{pr_number}](#{repo_url}/pull/#{pr_number})" if pull_request.present?

    # We don't want to send more than one message for this SHA with the same conclusion within 20 minutes.
    # This counter expires from Redis in 20 minutes.
    ci_counter = Redis::CI.new("check_suite_#{conclusion}_#{sha}")
    ci_counter.sucess_count_incr
    ActionCable.server.broadcast 'smokedetector_messages', message: message if ci_counter.sucess_count == 1
  end

  # This is invoked by the route: /github/report_check_run_failure
  # It's triggered by GitHub Action check runs in order to provide status to SmokeDetector to forward to chat.
  # Any conclusion other than "success" is forwarded to chat for the master branch or any PR.
  # It is not triggered by external CI providers (e.g. CircleCI and Travis CI).
  # A check_run event happens for each check that's run as part of CI testing. This is two events,
  #   one "created" and one "completed" for each GitHub Action workflow/matrix which is executed.
  # The check_suite event hook might be better, for some use cases, as it will have only one event for all the
  #   checks which are dispatched for a particular situation. Unfortunately, that event doesn't have some of the
  #   data which it is desirable to show to the users in chat.
  # Concerns:
  #   When a PR is created directly from a branch at the same time as the branch, we can get called for two
  #     concurrent runs, but we only want to report one. In the case that's of interest, we get two "created"
  #     messages and then two "completed" messages. This is resolved by using a Redis success counter to track that
  #     we don't send more than one message about the same commit SHA with the same result/conclusion within 20 minutes.
  def report_check_run_failure
    data = params
    check_run = data[:check_run]
    check_run_status = check_run[:status]
    sha = check_run[:head_sha]
    workflow_name = check_run[:name]
    conclusion = check_run[:conclusion]
    check_run_url = check_run[:html_url]
    check_suite = check_run[:check_suite]
    app_name = check_run[:app][:name]
    details_url = check_run[:details_url]
    pull_requests = check_suite[:pull_requests]
    pull_request = pull_requests[0]
    branch = check_suite[:head_branch]
    repository = data[:repository]
    repo_name = repository[:name]
    repo_url = repository[:html_url]
    sender_login = data[:sender][:login]
    pr_number = pull_request[:number] if pull_request.present?
    branch_path = ''
    branch_path = "/tree/#{branch}" if branch != 'master'

    # We are only interested in completed non-success
    return if check_run_status != 'completed' || conclusion == 'success'

    message = "[ [#{repo_name}](#{repo_url}) ]"
    message += if app_name == 'GitHub Actions'
                 " GitHub Action workflow [#{workflow_name}](#{check_run_url}):"
               else
                 " Check run [#{workflow_name}](#{check_run_url}):"
               end
    message += if pull_request.present?
                 " [#{conclusion}](#{repo_url}/pull/#{pr_number}/checks?sha=#{sha})"
               else
                 " [#{conclusion}](#{details_url})"
               end
    message += " on [#{sha.first(7)}](#{repo_url}/commit/#{sha.first(10)})"
    message += " by #{sender_login}" if sender_login.present?
    message += " in the [#{branch}](#{repo_url}#{branch_path}) branch" if branch.present?
    message += " for [PR ##{pr_number}](#{repo_url}/pull/#{pr_number})" if pull_request.present?

    # We don't want to send more than one message for this workflow & sha with the same conclusion within 20 minutes.
    # This counter expires from Redis in 20 minutes.
    ci_counter = Redis::CI.new("check_run_#{workflow_name}_#{conclusion}_#{sha}")
    ci_counter.sucess_count_incr
    ActionCable.server.broadcast 'smokedetector_messages', message: message if ci_counter.sucess_count == 1
  end

  # This is invoked by the route: /github/project_status
  # GitHub fires the status WebHook when there's a status update reported to GitHub via the GitHub API.
  # It fires when an external CI service updates status (e.g. finishes).
  # It is not fired for GitHub Actions. GitHub does fire this WebHook for GitHub Pages builds.
  # It's used to pass repository status changes to SmokeDetector for display in SE chat.
  # For the metasmoke repository:
  #   In order for success to be reported for 'ci/circleci' contexts, then this endpoint needs to be
  #   called three times within 20 minutes with the same SHA and 'success'. This is done because the
  #   metasmoke repository has three jobs run on CircleCI for each CI run and we only want to report
  #   success when all of them pass.
  #   States other than success will be reported with only one call to this endpoint.
  def any_status_hook
    repo = params[:name]
    link = "https://github.com/#{repo}"
    sha = params[:sha]
    state = params[:state]
    context = params[:context]
    description = params[:description]
    target = params[:target_url]

    return if state == 'pending' || (state == 'success' && context == 'github/pages')

    if repo == 'Charcoal-SE/metasmoke' && context.start_with?('ci/circleci')
      ci_counter = Redis::CI.new(sha)
      if state == 'success'
        ci_counter.sucess_count_incr
        return unless ci_counter.sucess_count == 3
        context = 'ci/circleci'
      else
        ci_counter.sucess_count_reset
      end
    end

    message = "[ [#{repo.sub('Charcoal-SE/', '')}](#{link}) ]"
    message += " #{context} "
    message += if target.present?
                 "[#{state}](#{target})"
               else
                 state
               end
    message += " on [#{sha.first(7)}](#{link}/commit/#{sha.first(10)})"
    message += ": #{description}" if description.present?

    ActionCable.server.broadcast 'smokedetector_messages', message: message
  end

  # This is invoked from /github/pr_merge.
  def pullapprove_merge_hook
    context = params[:context]
    state = params[:state]
    target = params[:target_url]

    if context == 'code-review/pullapprove' && state == 'success'
      pr_num = %r{https?:\/\/pullapprove\.com\/Charcoal-SE\/SmokeDetector\/pull-request\/(\d+)\/?}.match(target)[1].to_i
      pr = Octokit.client.pull_request('Charcoal-SE/SmokeDetector', pr_num)

      if pr[:user][:login] != 'SmokeDetector'
        render plain: "Not a blacklist PR, not merging (##{pr_num})"
        return
      end

      unless Dir.exist?('SmokeDetector')
        system 'git clone git@github.com:Charcoal-SE/SmokeDetector'

        Dir.chdir('SmokeDetector') do
          system 'git config user.name metasmoke'
          system 'git', 'config', 'user.email', AppConfig['github']['username']
        end
      end

      if !Octokit.client.pull_merged?('Charcoal-SE/SmokeDetector', pr_num)
        File.open('SmokeDetector/.git/info/attributes', File::RDWR) do |f|
          f.flock(File::LOCK_EX)

          Dir.chdir('SmokeDetector') do
            ref = pr[:head][:ref]

            system 'git fetch origin master; git checkout -B master origin/master'
            system 'git', 'fetch', 'origin', ref
            system 'git', 'merge', "origin/#{ref}", '--no-ff', '-m', "Merge pull request ##{pr_num} from Charcoal-SE/#{ref} --autopull"
            system 'git push origin master'
            system 'git', 'push', 'origin', '--delete', ref
            system 'git', 'branch', '-D', ref
          end
        end

        message = "Merged SmokeDetector [##{pr_num}](https://github.com/Charcoal-SE/SmokeDetector/pull/#{pr_num})."
        ActionCable.server.broadcast('smokedetector_messages', message: message)
        render plain: "Merged ##{pr_num}"
      else
        render plain: "##{pr_num} already merged"
      end
    else
      render plain: 'Not PullApprove successful status, ignoring'
    end
  end

  def add_pullapprove_comment
    pr_num = params[:number]
    Octokit.client.add_comment 'Charcoal-SE/SmokeDetector', pr_num, '!!/approve'
    render plain: 'OK'
  end

  private

  def verify_github
    signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), AppConfig['github']['secret_token'], request.raw_post)
    render(plain: "You're not GitHub!", status: 403) && return unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE'])
  end
end


text:
# frozen_string_literal: true

class SmokeDetectorsController < ApplicationController
  before_action :authenticate_user!, except: %i[audits check_token]
  before_action :verify_admin, except: %i[audits force_failover force_pull mine token_regen new create check_token]
  before_action :verify_blacklist_manager, only: %i[force_failover force_pull]
  before_action :verify_smoke_detector_runner, only: %i[mine token_regen new create]
  before_action :set_smoke_detector, except: %i[audits mine new create check_token]

  def destroy
    unless current_user.present? && (current_user.has_role?(:admin) || current_user.id == @smoke_detector.user_id)
      raise ActionController::RoutingError, 'Not Found'
    end

    if @smoke_detector.destroy
      flash[:success] = "Removed SmokeDetector key for #{@smoke_detector.location}"
    else
      flash[:danger] = "Can't remove key. If Undo's gone rogue, start running."
    end

    redirect_to params[:redirect] || status_path
  end

  def force_failover
    @smoke_detector.update(force_failover: true)
    flash[:success] = "Failover for #{@smoke_detector.location} will be forced on the next ping; probably within 60 seconds."

    redirect_to status_path
  end

  def force_pull
    @smoke_detector.update(force_pull: true)
    flash[:success] = "#{@smoke_detector.location} will pull updates on the next ping; probably within 60 seconds."

    redirect_to status_path
  end

  def audits
    @audits = Audited::Audit.where(auditable_type: 'SmokeDetector')
                            .includes(:auditable, :user)
                            .order(Arel.sql('created_at DESC'))
                            .paginate(page: params[:page], per_page: 100)
  end

  def mine
    @smoke_detectors = current_user.smoke_detectors
  end

  def new
    @smoke_detector = current_user.smoke_detectors.new
  end

  def create
    @smoke_detector = SmokeDetector.new(smoke_detector_params)
    @smoke_detector.location = @smoke_detector.name

    @smoke_detector.last_ping = 100.years.ago
    @smoke_detector.email_date = 99.years.ago

    @smoke_detector.access_token = SecureRandom.uuid
    @smoke_detector.user = current_user

    if @smoke_detector.save
      flash[:success] = "Successfully created new token: #{@smoke_detector.access_token}"
    else
      flash[:danger] = 'Something went wrong when saving SmokeDetector'
    end

    redirect_to smoke_detector_mine_path
  end

  def token_regen
    unless current_user.present? && current_user.id == @smoke_detector.user_id
      raise ActionController::RoutingError, 'Not Found'
    end

    if @smoke_detector.update(access_token: SecureRandom.uuid)
      flash[:success] = "Access token for #{@smoke_detector.location} updated to #{@smoke_detector.access_token}"
    else
      flash[:danger] = 'Something went wrong'
    end

    redirect_to params[:redirect] || smoke_detector_mine_path
  end

  # Used by Helios to verify new tokens
  def check_token
    token = SmokeDetector.where(access_token: params[:token]).first
    payload = {
      exists: token.present?,
      owner_name: token&.user&.username,
      location: token&.location,
      created_at: token&.created_at,
      updated_at: token&.updated_at
    }

    render json: payload
  end

  private

  def set_smoke_detector
    @smoke_detector = SmokeDetector.find params[:id]
  end

  def smoke_detector_params
    params.require(:smoke_detector).permit(:name)
  end
end


text:
# frozen_string_literal: true

class SiteSettingsController < ApplicationController
  before_action :verify_admin
  skip_before_action :verify_authenticity_token, only: [:update]

  def index
    @settings = SiteSetting.all.paginate(page: params[:page], per_page: 20)
  end

  def update
    SiteSetting[params[:name]] = params[:value]
    render json: { success: true }, status: 202
  end

  def destroy
    SiteSetting.find(params[:id]).destroy
    flash[:success] = 'Removed setting.'
    redirect_to site_settings_path
  end
end


text:
# frozen_string_literal: true

include APIHelper

class CodeStatusController < ApplicationController
  def index
    @gem_versions = Gem::Specification.sort_by { |g| [g.name.downcase, g.version] }.sort_by(&:name)
    @important_gems = %w[
      rails
      will_paginate
      turbolinks
    ]
  end

  def api
    @repo = Rails.cache.fetch "code_status/repo##{CurrentCommit}" do
      Octokit.repository 'Charcoal-SE/metasmoke'
    end
    @compare = Rails.cache.fetch "code_status/compare##{CurrentCommit}" do
      Octokit.compare 'Charcoal-SE/metasmoke', CurrentCommit, @repo[:default_branch]
    end
    @compare_diff = Rails.cache.fetch "code_status/compare_diff##{CurrentCommit}" do
      Octokit.compare 'Charcoal-SE/metasmoke', CurrentCommit, @repo[:default_branch], accept: 'application/vnd.github.v3.diff'
    end
    @commit = Rails.cache.fetch "code_status/commit##{CurrentCommit}" do
      Octokit.commit 'Charcoal-SE/metasmoke', CurrentCommit
    end
    @commit_diff = Rails.cache.fetch "code_status/commit_diff##{CurrentCommit}" do
      Octokit.commit 'Charcoal-SE/metasmoke', CurrentCommit, accept: 'application/vnd.github.v3.diff'
    end
  end
end


text:
# frozen_string_literal: true

class SpamDomainsController < ApplicationController
  before_action :check_if_smokedetector, only: [:create_from_post]
  before_action :authenticate_user!, only: %i[edit update destroy create new]
  before_action :verify_core, only: %i[edit update create new fix_asn_tags]
  before_action :verify_admin, only: [:destroy]
  before_action :set_spam_domain, only: %i[show edit update destroy fix_asn_tags]

  def index
    @total = SpamDomain.count
    @domains = if params[:filter].present?
                 SpamDomain.where('domain LIKE ?', "%#{params[:filter]}%")
               else
                 SpamDomain.all
               end.includes(:domain_tags).order(domain: :asc).paginate(page: params[:page], per_page: 100)
    SpamDomain.preload_post_counts(@domains)
  end

  def fix_asn_tags
    @domain.fix_asn_tags
    redirect_to action: :show, id: @domain.id
  end

  def create_from_post
    @post = Post.find params[:post_id]
    domains = params[:domains]
    domains.each do |d|
      record = SpamDomain.find_or_create_by domain: d
      @post.spam_domains << record
    end

    render json: { status: 'success', total_domains: @post.spam_domains.count }
  end

  def show
    @counts = { all: SpamDomain.where(id: params[:id]).joins(:posts).count,
                tp: SpamDomain.where(id: params[:id]).joins(:posts).where(posts: { is_tp: true }).count }
    @posts = @domain.posts.order(created_at: :desc).includes_for_post_row.paginate(page: params[:page], per_page: 100)
    @sites = Site.where(id: @posts.map(&:site_id))
  end

  def edit; end

  def new
    @domain = SpamDomain.new
  end

  def create
    @domain = SpamDomain.new domain_params
    if @domain.save
      redirect_to action: :show, id: @domain.id
    else
      flash[:danger] = @domain.errors.full_messages
      redirect_back fallback_location: request.path
    end
  end

  def update
    if @domain.update(domain_params.tap { |d| d.delete(:domain) })
      flash[:success] = 'Updated successfully.'
      redirect_to spam_domain_path(@domain)
    else
      flash[:danger] = 'Failed to update.'
      render :edit
    end
  end

  def destroy
    if @domain.destroy
      flash[:success] = 'Removed domain.'
      redirect_to spam_domains_path
    else
      flash[:danger] = 'Failed to remove domain.'
      render :show
    end
  end

  def query
    render json: SpamDomain.where('domain LIKE ?', "%#{params[:q]}%").map { |d| { value: d.id, text: d.domain } }
  end

  private

  def set_spam_domain
    @domain = SpamDomain.find params[:id]
  end

  def domain_params
    params.require(:spam_domain).permit(:whois, :domain)
  end
end


text:
# frozen_string_literal: true

class UsersController < ApplicationController
  before_action :authenticate_user!, except: [:missing_privileges]
  before_action :verify_admin, only: %i[show send_password_reset]
  before_action :verify_developer, only: %i[refresh_ids update_mod_sites]
  before_action :set_user, only: %i[show refresh_ids send_password_reset update_mod_sites]

  def username; end

  def set_username
    current_user.username = params[:username]
    current_user.save!

    redirect_to dashboard_path
  end

  def apps
    @keys = APIKey.find(current_user.api_tokens.pluck(:api_key_id))
  end

  def revoke_app
    @key = APIKey.find params[:key_id]
    @tokens = APIToken.where(api_key: @key, user: current_user)
    if @tokens.destroy_all
      flash[:success] = "Revoked access to your account from #{@key.app_name}."
    else
      flash[:danger] = 'Could not revoke access - contact a metasmoke admin.'
    end
    redirect_to url_for(controller: :users, action: :apps)
  end

  def tf_status; end

  def enable_2fa
    secret = ROTP::Base32.random_base32
    current_user.update(two_factor_token: secret)
    totp = ROTP::TOTP.new(secret, issuer: 'metasmoke')
    uri = totp.provisioning_uri("#{current_user.id}@metasmoke.erwaysoftware.com")
    qr_svg = RQRCode::QRCode.new(uri).as_svg
    @qr_uri = "data:image/svg+xml;base64,#{Base64.encode64(qr_svg)}"
  end

  def enable_code; end

  def confirm_enable_code
    if current_user.two_factor_token.blank?
      flash[:danger] = "Missed a step! There's no 2FA token on your account."
      redirect_to(url_for(controller: :users, action: :tf_status)) && return
    end

    totp = ROTP::TOTP.new(current_user.two_factor_token)
    if totp.verify(params[:code], drift_behind: 30, drift_ahead: 30)
      current_user.update(enabled_2fa: true)
      flash[:success] = 'Success! 2FA has been enabled on your account.'
      redirect_to url_for(controller: :users, action: :tf_status)
    else
      flash[:danger] = "That's not the right code."
      redirect_to url_for(controller: :users, action: :enable_code)
    end
  end

  def disable_code; end

  def confirm_disable_code
    if current_user.two_factor_token.blank?
      flash[:danger] = "I don't know how you got here, but something is badly wrong."
      redirect_to(url_for(controller: :users, action: :tf_status)) && return
    end

    totp = ROTP::TOTP.new(current_user.two_factor_token)
    if totp.verify(params[:code], drift_behind: 30, drift_ahead: 30)
      current_user.update(two_factor_token: nil, enabled_2fa: false)
      flash[:success] = 'Success! 2FA has been disabled on your account.'
      redirect_to url_for(controller: :users, action: :tf_status)
    else
      flash[:danger] = "That's not the right code."
      redirect_to url_for(controller: :users, action: :disable_code)
    end
  end

  def set_announcement_emails
    emails = params[:emails].present?
    if current_user.update(announcement_emails: emails)
      flash[:success] = (emails ? 'Subscribed to' : 'Unsubscribed from') + ' announcement emails.'
    else
      flash[:danger] = 'Failed to save your preferences.'
    end
    redirect_to edit_user_registration_path
  end

  def update_email
    unless /\d+@se-oauth\.metasmoke/.match?(current_user.email)
      flash[:danger] = 'Your email is not the default for an SE OAuth created account.'
      redirect_to edit_user_registration_path
      return
    end

    if current_user.update(email: params[:email])
      flash[:success] = 'Email address updated. You will need to sign out and click "Forgot your password?" on the sign-in page to set your password.'
    else
      flash[:danger] = 'Failed to update your email address.'
    end
    redirect_to edit_user_registration_path
  end

  def missing_privileges
    @role = Role.find_by(name: params[:required])
  end

  def flagging_enabled
    @users = User.where(flags_enabled: true).paginate(per_page: 100, page: params[:page])
  end

  def show; end

  def refresh_ids
    @user.update_chat_ids
    flash[:success] = "Chat IDs refreshed for #{@user.username}."
    redirect_to dev_user_path(@user)
  end

  def send_password_reset
    @user.send_reset_password_instructions
    flash[:success] = "Reset email sent to #{@user.username}."
    redirect_to dev_user_path(@user)
  end

  def update_mod_sites
    @user.update_moderator_sites
    flash[:success] = "Refreshed mod sites list for #{@user.username}."
    redirect_to dev_user_path(@user)
  end

  def migrate_token_confirmation
    return unless params[:state] != Rails.cache.fetch("token_migration_state/#{current_user.id}")
    flash[:danger] = "It looks like you didn't actually authenticate! It might still work, but I don't think it will."
  end

  def migrate_token
    state = Rails.cache.fetch("token_migration_state/#{current_user.id}")
    res = HTTParty.get("#{AppConfig['token_store']['host']}/auth/confirm",
                       headers: { 'X-API-Key': AppConfig['token_store']['key'] },
                       query: { account_id: current_user.stack_exchange_account_id, state: state })
    Rails.logger.info res
    if params[:state] == state && JSON.parse(res.body)['token_exists'] && current_user.update(write_authenticated: true)
      flash[:success] = 'Your registration was completed sucessfully!'
    elsif params[:error].present?
      flash[:danger] = "Got an error: #{params[:error]}: #{params[:error_description]}"
    else
      flash[:danger] = "We couldn't complete your registration. Please try again."
    end
    redirect_to edit_user_registration_path
  end

  private

  def set_user
    @user = User.find params[:id]
  end
end


text:
# frozen_string_literal: true

class FlagLogController < ApplicationController
  respond_to :html, :js

  def index
    @individual_user = User.find(params[:user_id]) if params[:user_id].present?

    @applicable_flag_logs = if @individual_user
                              @individual_user.flag_logs.where(is_auto: true)
                            else
                              FlagLog.all
                            end

    @applicable_flag_logs = case params[:filter]
                            when 'fps'
                              @applicable_flag_logs.joins(:post)
                                                   .where(success: true)
                                                   .where(posts: { is_tp: false })
                                                   .where('`posts`.`is_fp` = 1 OR `posts`.`is_naa` = 1')
                            when 'failures'
                              @applicable_flag_logs.where(success: false)
                            when 'manual'
                              @individual_user ? @individual_user.flag_logs.where(auto: false) : FlagLog.manual
                            when 'other'
                              @applicable_flag_logs.unscoped.where(flag_type: 'other')
                            else
                              @applicable_flag_logs.auto.spam
                            end

    @flag_logs = @applicable_flag_logs.order(Arel.sql('flag_logs.created_at DESC, flag_logs.id DESC'))
                                      .includes(post: [feedbacks: %i[user api_key]])
                                      .includes(post: [:reasons])
                                      .includes(:user)
                                      .paginate(page: params[:page], per_page: 100)
    @sites = Site.where(id: @flag_logs.map(&:post).map(&:site_id)).to_a
  end

  def by_post
    @individual_post = Post.find(params[:id])
    @flag_logs = @individual_post.flag_logs
                                 .order(Arel.sql('created_at DESC, id DESC'))
                                 .includes(post: [feedbacks: %i[user api_key]])
                                 .includes(post: [:reasons])
                                 .includes(:user)
                                 .paginate(page: params[:page], per_page: 100)
    @sites = Site.where(id: @flag_logs.map(&:post).map(&:site_id)).to_a
    render :index
  end

  def eligible_flaggers
    @eligible_users = Post.find(params[:id]).eligible_flaggers
  end

  def not_flagged
    @posts = Post.left_joins(:flag_logs)
                 .where(flag_logs: { id: nil })
                 .includes(feedbacks: %i[user api_key])
                 .includes(:reasons)
                 .order(created_at: :desc)
                 .paginate(page: params[:page], per_page: 100)
    @sites = Site.where(id: @posts.map(&:site_id)).to_a
  end
end


text:
# frozen_string_literal: true

class UserSiteSettingsController < ApplicationController
  before_action :authenticate_user!
  before_action :verify_admin, only: [:for_user]
  before_action :set_preference, only: %i[edit update destroy]
  before_action :verify_authorized, only: %i[edit update destroy]

  def index
    @preferences = UserSiteSetting.where(user: current_user)
  end

  def for_user
    @user = User.find params[:user]
    @preferences = UserSiteSetting.where(user: @user)
  end

  def enable_flagging
    if (FlagSetting['registration_enabled'] == '0' || !current_user.has_role?(:flagger)) && !current_user.flags_enabled
      render json: { status: 'nope' }, status: 500
      return
    end

    if current_user.update(flags_enabled: params[:enable])
      render json: { status: 'ok' }
    else
      render json: { status: 'nope' }, status: 500
    end
  end

  def new
    @preference = UserSiteSetting.new
  end

  def create
    @preference = UserSiteSetting.new preference_params
    @preference.user = current_user
    @preference.site_ids = params[:user_site_setting][:sites]

    if @preference.save
      flash[:success] = 'Saved your preferences.'
      redirect_to url_for(controller: :user_site_settings, action: :index)
    else
      render :new, status: 422
    end
  end

  def edit; end

  def update
    @preference.sites = params[:user_site_setting][:sites].map { |i| Site.find i.to_i if i.present? }.compact
    if @preference.update preference_params
      flash[:success] = 'Saved your preferences.'
      redirect_to url_for(controller: :user_site_settings, action: :index)
    else
      render :edit, status: 422
    end
  end

  def destroy
    if @preference.destroy
      flash[:success] = 'Removed your preferences.'
    else
      flash[:danger] = 'Failed to remove your preferences - contact a developer to find out why.'
    end
    redirect_to url_for(controller: :user_site_settings, action: :index)
  end

  private

  def set_preference
    @preference = UserSiteSetting.find params[:id]
  end

  def preference_params
    params.require(:user_site_setting).permit(:max_flags)
  end

  def verify_authorized
    return if current_user.has_role?(:admin) || @preference.user == current_user
    raise ActionController::RoutingError, 'Not Found'
  end
end


text:
# frozen_string_literal: true

class StatisticsController < ApplicationController
  protect_from_forgery except: [:create]
  before_action :check_if_smokedetector, only: [:create]

  # GET /statistics
  def index
    @smoke_detector = SmokeDetector.find(params[:id])
    @statistics = @smoke_detector.statistics.paginate(page: params[:page], per_page: 100).order(Arel.sql('created_at DESC'))
  end

  # POST /statistics.json
  def create
    @statistic = Statistic.new(statistic_params)

    if @statistic.posts_scanned == 0 || @statistic.api_quota == -1
      render(plain: 'OK', status: :created) && return
    end

    @statistic.smoke_detector = @smoke_detector

    respond_to do |format|
      if @statistic.save
        format.json { render plain: 'OK', status: :created }
      else
        format.json { render json: @statistic.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  # Never trust parameters from the scary internet, only allow the white list through.
  def statistic_params
    params.require(:statistic).permit(:posts_scanned, :api_quota, :post_scan_rate)
  end
end


text:
# frozen_string_literal: true

class AbuseReportStatusesController < ApplicationController
  before_action :verify_core, except: %i[index show]
  before_action :set_status, except: %i[index create]
  before_action :verify_admin, only: :destroy

  def index
    @statuses = AbuseReportStatus.all
  end

  def create
    @status = AbuseReportStatus.new status_params
    if @status.save
      flash[:success] = 'Created status.'
      redirect_back fallback_location: abuse_status_path(@status)
    else
      flash[:danger] = 'Failed to create status.'
      redirect_to abuse_statuses_path
    end
  end

  def show
    @reports = @status.reports.includes(:contact, :status, :user)
  end

  def edit; end

  def update
    if @status.update status_params
      flash[:success] = 'Updated status.'
      redirect_to abuse_status_path(@status)
    else
      flash[:danger] = 'Failed to update.'
      render :edit
    end
  end

  def destroy
    if @status.destroy
      flash[:success] = 'Removed status.'
      redirect_to abuse_statuses_path
    else
      flash[:danger] = 'Failed to remove.'
      redirect_to abuse_status_path(@status)
    end
  end

  private

  def set_status
    @status = AbuseReportStatus.find params[:id]
  end

  def status_params
    params.require(:abuse_report_status).permit(:name, :details, :icon, :color)
  end
end


text:
# frozen_string_literal: true

class AbuseReportsController < ApplicationController
  before_action :verify_core, except: [:public_link]
  before_action :set_report, except: %i[index new create public_link]
  before_action :verify_access, except: %i[index new create show public_link]
  before_action :verify_admin, only: [:destroy]

  def index
    case params[:filter]
    when 'closed'
      ids = [AbuseReportStatus['Closed: Successful'].id, AbuseReportStatus['Closed: Unsuccessful'].id]
      @reports = AbuseReport.where(abuse_report_status_id: ids).paginate(page: params[:page], per_page: 90)
    when 'stale'
      @reports = AbuseReport.where(status: AbuseReportStatus['Stale']).paginate(page: params[:page], per_page: 90)
    when 'open'
      @reports = AbuseReport.where(status: AbuseReportStatus['Open']).paginate(page: params[:page], per_page: 90)
    else
      @reports = AbuseReport.where(status: AbuseReportStatus['Open']).paginate(page: params[:page], per_page: 90)
    end
  end

  def new; end

  def create
    @report = AbuseReport.new report_params.merge(user: current_user)
    if @report.save
      flash[:success] = "Opened new abuse report ##{@report.id}."
      redirect_to abuse_report_path(@report)
    else
      flash[:danger] = "Failed to open new report:\n#{@report.errors.full_messages.join("\n")}"
      redirect_to new_abuse_report_path(reportable_type: @report.reportable_type, reportable_id: @report.reportable_id)
    end
  end

  def show
    @reportable_path = {
      'DomainTag' => domain_tag_path(@report.reportable),
      'Post' => post_path(@report.reportable),
      'SpamDomain' => spam_domain_path(@report.reportable)
    }[@report.reportable_type]
  end

  def public_link
    @report = AbuseReport.find_by_uuid params[:uuid]
    @reportable_path = {
      'DomainTag' => domain_tag_path(@report.reportable),
      'Post' => post_path(@report.reportable),
      'SpamDomain' => spam_domain_path(@report.reportable)
    }[@report.reportable_type]
    render :show
  end

  def update
    if @report.update report_params
      flash[:success] = 'Updated report.'
    else
      flash[:danger] = 'Failed to update report.'
    end
    redirect_back fallback_location: abuse_report_path(@report)
  end

  def update_status
    old_status = @report.status.name
    status = AbuseReportStatus.find params[:status_id]
    new_status = status.name
    if @report.update status: status
      AbuseComment.create user: User.find(-1), report: @report,
                          text: "**#{current_user.username}** updated the status from **#{old_status}** to **#{new_status}**"
      flash[:success] = 'Updated status.'
    else
      flash[:danger] = 'Failed to update status.'
    end
    redirect_back fallback_location: abuse_report_path(@report)
  end

  def destroy
    if @report.destroy
      flash[:success] = 'Removed report.'
      redirect_to abuse_reports_path
    else
      flash[:danger] = 'Failed to remove report.'
      redirect_to abuse_report_path(@report)
    end
  end

  private

  def report_params
    params.require(:abuse_report).permit(:reportable_type, :reportable_id, :abuse_contact_id, :details)
  end

  def set_report
    @report = AbuseReport.find params[:id]
  end

  def verify_access
    return if current_user == @report.user || current_user&.has_role?(:admin)
    not_found
  end
end


text:
# frozen_string_literal: true

class StatusController < ApplicationController
  include ActionView::Helpers::DateHelper

  protect_from_forgery except: [:status_update]
  before_action :check_if_smokedetector, only: [:status_update]
  before_action :verify_admin, only: [:kill]

  def index
    @statuses = SmokeDetector.order(Arel.sql('last_ping DESC')).all
    @scans = Statistic.where('created_at >= ?', 24.hours.ago).where.not(posts_scanned: nil)
                      .select(Arel.sql('smoke_detector_id, SUM(posts_scanned) as scans'))
                      .group(:smoke_detector_id).map { |s| [s.smoke_detector_id, s.scans] }.to_h
  end

  def status_update
    @smoke_detector.last_ping = DateTime.now
    @smoke_detector.location = params[:location]
    @smoke_detector.is_standby = params[:standby] || false

    # If an instance is manually switched to standby, we
    # don't want it to immediately kick back
    new_standby_switch = @smoke_detector.is_standby_changed? && @smoke_detector.is_standby

    @smoke_detector.save!

    ActionCable.server.broadcast 'status', status_channel_data
    ActionCable.server.broadcast 'status_blacklist_manager', status_channel_data.merge(failover_link: failover_link)
    ActionCable.server.broadcast 'topbar', last_ping: @smoke_detector.last_ping.to_f
    ActionCable.server.broadcast 'smokey_pings', smokey: @smoke_detector.as_json

    respond_to do |format|
      format.json do
        if @smoke_detector.should_failover && !new_standby_switch
          @smoke_detector.update(is_standby: false, force_failover: false)
          render status: 200, json: { failover: true }
        elsif @smoke_detector.should_pull
          @smoke_detector.update(force_pull: false)
          render status: 200, json: { pull_update: true }
        else
          head 200, content_type: 'text/html'
        end
      end
    end
  end

  def kill
    ActionCable.server.broadcast 'smokedetector_messages', message: { everything_is_broken: true }.to_json
    flash[:success] = 'Kill command sent. I hope you know what you\'re doing.'
    redirect_to status_path
  end

  private

  def status_channel_data
    {
      id: @smoke_detector.id,
      ts_unix: @smoke_detector.last_ping.to_i,
      ts_ago: time_ago_in_words(@smoke_detector.last_ping, include_seconds: true),
      ts_raw: @smoke_detector.last_ping.to_s,
      location: @smoke_detector.location,
      is_standby: @smoke_detector.is_standby,
      active: active?
    }
  end

  def failover_link
    active? && @smoke_detector.is_standby && smoke_detector_force_failover_path(@smoke_detector.id)
  end

  def active?
    @active ||= @smoke_detector.last_ping > 3.minutes.ago
  end
end


text:
# frozen_string_literal: true

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :redis_log_request
  before_action :check_auth_required
  before_action :deduplicate_ajax_requests
  before_action :maybe_store_user_location

  before_action do
    Rack::MiniProfiler.authorize_request if current_user&.has_role?(:developer)
  end

  def check_if_smokedetector
    Rack::MiniProfiler.step('ApplicationController: check_if_smokedetector') do
      provided_key = params[:key]

      @smoke_detector = SmokeDetector.find_by(access_token: provided_key)

      return if @smoke_detector.present? # Authorized
      render(plain: 'Go away', status: 403)
    end
  end

  def not_found
    raise ActionController::RoutingError, 'Not Found'
  end

  def after_sign_in_path_for(resource_or_scope)
    if current_user.username.nil?
      users_username_path
    elsif current_user.stack_exchange_account_id.nil?
      authentication_status_path
    else
      stored_location = nil
      begin
        stored_location = stored_location_for(resource_or_scope)
      rescue # rubocop:disable Lint/HandleExceptions
      end

      request.env['omniauth.origin'] || stored_location || root_path
    end
  end

  def redis_log(_msg)
    # redis = redis(logger: true)
    # redis.multi do |m|
    #   m.rpush "#{redis_log_key}/logs", msg.to_s
    #   m.expire "#{redis_log_key}/logs", REDIS_LOG_EXPIRATION
    # end
  end

  protected

  Role.names.each do |rn|
    define_method "verify_#{rn}" do
      return if user_signed_in? && current_user.has_role?(rn)
      redirect_to missing_privileges_path(required: rn)
    end
  end

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: %i[username eu_resident privacy_accepted])
    devise_parameter_sanitizer.permit(:account_update, keys: [:username])
  end

  def verify_at_least_one_diamond
    return if user_signed_in? && current_user.moderator_sites.exists?
    redirect_to missing_privileges_path(required: :at_least_one_diamond)
  end

  private

  def smokey_route?
    {
      posts: :create,
      feedbacks: :create,
      deletion_logs: :create,
      statistics: :create,
      status: :status_update
    }.dig(controller_name.to_sym) == action_name.to_sym
  end

  def redis_log_request
    Rack::MiniProfiler.step('Logging to redis') do
      # redis = redis(logger: true)
      @request_time ||= Time.now.to_f
      request.set_header 'redis_logs.log_key', redis_log_key
      request.set_header 'redis_logs.timestamp', @request_time
      request.set_header 'redis_logs.request_id', request.uuid
      # unless session[:redis_log_id].present? || smokey_route?
      #   session[:redis_log_id] = SecureRandom.base64
      #   session[:redis_log_id] = SecureRandom.base64 while redis.exists("session/#{session[:redis_log_id]}")
      # end
      # RedisLogJob.perform_later(
      #   {
      #     path: request.filtered_path,
      #     impersonator_id: session[:impersonator_id],
      #     user_id: user_signed_in? ? current_user.id : nil,
      #     session_id: smokey_route? ? params[:key] : session[:redis_log_id],
      #     sha: CurrentCommit,
      #     smoke_detector_id: smokey_route? ? SmokeDetector.find_by(access_token: params[:key])&.id : nil
      #   },
      #   subspaces: {
      #     request_headers: headers.to_h,
      #     params: request.filtered_parameters.except(:controller, :action)
      #   },
      #   time: @request_time,
      #   uuid: request.uuid,
      #   session_id: session[:redis_log_id],
      #   user_id: user_signed_in? ? current_user.id : nil
      # )
    end
  end

  def redis_log_key
    # We include the time just to doubly ensure that the uuid is unique
    "request/#{@request_time}/#{request.uuid}"
  end

  def check_auth_required
    return unless SiteSetting['require_auth_all_pages']
    return if user_signed_in? || devise_controller? || (controller_name == 'users' && action_name == 'missing_privileges')

    return if smokey_route?

    redis_log 'Redirected to login page because require_auth_all_pages is set'
    flash[:warning] = 'You need to have an account to view metasmoke pages.'
    authenticate_user!
  end

  def deduplicate_ajax_requests
    return unless request.headers['X-AJAX-Deduplicate'].present?
    request_uniq = request.headers['X-AJAX-Deduplicate']
    new_request = redis.set "request-dedup/#{request_uniq}", 1, ex: 300, nx: true
    return if new_request
    redis_log 'Rejected by ajax deduplication'
    render status: :conflict, plain: "409 Conflict\nRequest conflicts with a previous AJAX request"
  end

  # Maximum length of path to be stored in cookie for redirecting back
  MAX_STORE_PATH_LENGTH = 1000

  def maybe_store_user_location
    return unless request.get? && is_navigational_format? && !devise_controller? && !request.xhr?

    if request.fullpath.length < MAX_STORE_PATH_LENGTH
      store_location_for(:user, request.fullpath)
    elsif request.path.length < MAX_STORE_PATH_LENGTH
      store_location_for(:user, request.path)
    end
  end
end


text:
# frozen_string_literal: true

class AdminController < ApplicationController
  before_action :verify_admin, only: %i[permissions update_permissions destroy_user]
  before_action :verify_developer, only: :destroy_user

  def index; end

  def recently_invalidated
    @feedbacks = Feedback.unscoped.includes(:invalidated_by)
                         .joins('inner join posts on feedbacks.post_id = posts.id')
                         .where(is_invalidated: true)
                         .select(Arel.sql('posts.title, feedbacks.*'))
                         .order(Arel.sql('feedbacks.invalidated_at DESC'))
                         .paginate(page: params[:page], per_page: 100)
  end

  def user_feedback
    @user = User.all.where(id: params[:user_id]).first
    @feedback = Feedback.unscoped
                        .joins('inner join posts on feedbacks.post_id = posts.id')
                        .where(user_id: @user.id)
                        .select(Arel.sql('posts.title, feedbacks.*'))
    @sources = ['metasmoke']

    @sources << 'Stack Overflow chat' if @user.stackoverflow_chat_id.present?

    @sources << 'Stack Exchange chat' if @user.stackexchange_chat_id.present?

    if @user.meta_stackexchange_chat_id.present?
      @sources << 'Meta Stack Exchange chat'
    end

    @feedback = @feedback.order(Arel.sql('feedbacks.id DESC')).paginate(page: params[:page], per_page: 1000)
    @feedback_count = @feedback.count
    @invalid_count = @feedback.where(is_invalidated: true).count
    @feedback_count_today = @feedback.where('feedbacks.updated_at > ?', Date.today).count
  end

  def users
    @roles = Role.names
    @users = if params[:filter].present?
               User.where('username LIKE ?', "%#{params[:filter]}%")
             else
               User.all
             end.includes(:roles).paginate(page: params[:page], per_page: 100)
  end

  def api_feedback
    @feedback = Feedback.via_api.includes(:user, :post).order(created_at: :desc).paginate(page: params[:page], per_page: 100)
  end

  def permissions
    redirect_to action: :users
  end

  def update_permissions
    if params[:permitted] == 'true'
      if params[:role] == 'developer'
        render plain: 'you must be a developer', status: :forbidden
        return
      end

      if params[:pinned]
        User.find(params[:user_id]).add_pinned_role params[:role]
      else
        User.find(params[:user_id]).add_role params[:role]
      end
    else
      User.find(params[:user_id]).remove_role params[:role]
    end

    render plain: 'success', status: :accepted
  end

  def destroy_user
    User.find(params[:user_id]).destroy
    render plain: 'success', status: :accepted
  end
end


text:
# frozen_string_literal: true

class FlagSettingsController < ApplicationController
  protect_from_forgery except: %i[smokey_disable_flagging update_site_settings]
  before_action :set_flag_setting, only: %i[edit update]
  before_action :verify_admin, except: %i[index audits smokey_disable_flagging dashboard by_site]
  before_action :authenticate_user!, except: %i[index audits smokey_disable_flagging dashboard by_site]
  before_action :check_if_smokedetector, only: [:smokey_disable_flagging]

  # GET /flag_settings
  # GET /flag_settings.json
  def index
    @flag_settings = FlagSetting.all
  end

  # GET /flag_settings/new
  def new
    @flag_setting = FlagSetting.new
  end

  # GET /flag_settings/1/edit
  def edit; end

  # POST /flag_settings
  # POST /flag_settings.json
  def create
    @flag_setting = FlagSetting.new(flag_setting_params)

    respond_to do |format|
      if @flag_setting.save
        format.html { redirect_to flag_settings_path, notice: 'Flag setting was successfully created.' }
        format.json { render :show, status: :created, location: @flag_setting }
      else
        format.html { render :new, status: 422 }
        format.json { render json: @flag_setting.errors, status: :unprocessable_entity }
      end
    end
  end

  def audits
    @audits = Audited::Audit.where(auditable_type: 'FlagSetting')
                            .includes(:auditable, :user)
                            .order(Arel.sql('created_at DESC'))
                            .paginate(page: params[:page], per_page: 100)
  end

  # PATCH/PUT /flag_settings/1
  # PATCH/PUT /flag_settings/1.json
  def update
    respond_to do |format|
      if @flag_setting.update(flag_setting_params)

        if %w[min_accuracy min_post_count].include? @flag_setting.name
          # If an accuracy/post count requirement is changed,
          # we want to re-validate all existing FlagConditions
          # and disable them if they aren't in compliance with the
          # new settings
          RevalidateFlagConditionsJob.perform_later
        end

        format.html { redirect_to flag_settings_path, notice: 'Flag setting was successfully updated.' }
        format.json { render :show, status: :ok, location: @flag_setting }
      else
        format.html { render :edit, status: 422 }
        format.json { render json: @flag_setting.errors, status: :unprocessable_entity }
      end
    end
  end

  # Used by SmokeDetector's !!/stopflagging
  def smokey_disable_flagging
    # -1 == System user in metasmoke prod
    Audited::Audit.as_user(User.find(-1)) do
      FlagSetting.find_by(name: 'flagging_enabled').update(value: '0')
    end

    SmokeDetector.send_message_to_charcoal('**Autoflagging disabled** through chat.')

    render plain: 'OK'
  end

  def dashboard
    @recent_count = FlagLog.auto.where('created_at > ?', 1.day.ago).where(success: true).count
  end

  def by_site
    if params[:site].present?
      @site = Site.find params[:site]
      @flags = FlagLog.where(site: @site)
      @posts = Post.includes_for_post_row.where(id: @flags.where.not(post_id: nil).map(&:post_id))
                   .paginate(per_page: 50, page: params[:page])
    end
    @sites = Site.mains.order(site_name: :asc)
  end

  def site_settings
    @sites = Site.mains.order(site_name: :asc)
    @json = JSON.dump(@sites.map do |s|
      [s.id, { flags_enabled: s.flags_enabled,
               max_flags: s.max_flags_per_post,
               auto_disputed_flags_enabled: s.auto_disputed_flags_enabled,
               name: s.site_name, domain: s.site_domain }]
    end.to_h)
  end

  def update_site_settings
    updata = params[:data]
    updata.each do |k, v|
      next unless v['changed'].present? && v['changed'] == true
      params = {}
      params[:flags_enabled] = v['flags_enabled'] unless v['flags_enabled'].nil?
      params[:max_flags_per_post] = v['max_flags'] unless v['max_flags'].nil?
      params[:auto_disputed_flags_enabled] = v['auto_disputed_flags_enabled'] unless v['auto_disputed_flags_enabled'].nil?
      site = Site.find(k)
      puts "Update: #{site} #{params}"
      site.update(params)
    end
    head :no_content
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_flag_setting
    @flag_setting = FlagSetting.find(params[:id])
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def flag_setting_params
    params.require(:flag_setting).permit(:name, :value)
  end
end


text:
# frozen_string_literal: true

class CustomRegistrationsController < Devise::RegistrationsController
  def create
    if SiteSetting['registration_enabled']
      super
    elsif SiteSetting['make_trolls_lives_hard']
      uri = ['https://www.youtube.com/watch?v=dQw4w9WgXcQ', 'https://speed.hetzner.de/10GB.bin'].sample
      redirect_to uri
    else
      flash[:danger] = 'Account registration is disabled.'
      redirect_back fallback_location: root_path
    end
  end

  def destroy
    if @user.stack_exchange_account_id.present?
      Rails.cache.write("deleted_user_#{@user.stack_exchange_account_id}", '1', expires_in: 1.hour)
    end

    super
  end
end


text:
# frozen_string_literal: true

class DeletionLogsController < ApplicationController
  before_action :set_deletion_log, only: %i[show edit update destroy]
  before_action :check_if_smokedetector, only: :create
  protect_from_forgery except: [:create]

  # POST /deletion_logs
  # POST /deletion_logs.json
  def create
    post_link = deletion_log_params[:post_link]

    post = Post.find_by(link: post_link)

    if post.nil?
      render plain: 'Error: No post found for link', status: 404
      return
    elsif post.deletion_logs.where(is_deleted: params[:deletion_log][:is_deleted])
              .where('uncertainty <= ?', params[:deletion_log][:uncertainty]).exists?
      render plain: 'Error: Deletion logs already exist', status: 409
      return
    end

    @deletion_log = post.deletion_logs.new
    @deletion_log.is_deleted = params[:deletion_log][:is_deleted]
    @deletion_log.uncertainty = params[:deletion_log][:uncertainty]

    respond_to do |format|
      if @deletion_log.save
        format.json { render plain: 'OK', status: :created }
      else
        format.json { render json: @deletion_log.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_deletion_log
    @deletion_log = DeletionLog.find(params[:id])
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def deletion_log_params
    params.require(:deletion_log).permit(:post_link, :is_deleted)
  end
end


text:
# frozen_string_literal: true

class AnnouncementsController < ApplicationController
  before_action :verify_core, except: [:index]
  before_action :verify_admin, only: [:expire]

  def index
    @announcements = Announcement.all.order(created_at: :desc)
  end

  def expire
    @announcement = Announcement.find(params[:id])

    if @announcement.update(expiry: DateTime.now)
      flash[:success] = 'Announcement expired'
    else
      flash[:danger] = 'Something went wrong'
    end

    redirect_to announcements_path
  end

  def new
    @announcement = Announcement.new
  end

  def create
    html = helpers.render_markdown(params[:text])
    date = Date.parse(params[:expiry_date])
    time = Time.parse(params[:expiry_time])
    expiry = DateTime.new(date.year, date.month, date.day, time.hour, time.min, time.sec, time.zone)

    @announcement = Announcement.new(text: html, expiry: expiry)
    if @announcement.save
      flash[:success] = 'Created your announcement.'
      AnnouncementsMailer.announce(@announcement).deliver_now
      redirect_to url_for(controller: :announcements, action: :new)
    else
      flash[:error] = "Couldn't create your announcement."
      render :new, status: 500
    end
  end
end


text:
# frozen_string_literal: true

class DomainLinksController < ApplicationController
  before_action :verify_core
  before_action :set_link, except: :create

  def create
    @link = DomainLink.new link_params.merge(creator: current_user)
    if @link.save
      Rails.cache.delete :domain_link_types
      render partial: 'domain_links/link', locals: { link: @link, domain: @link.left }
    else
      render json: { success: false }, status: 500
    end
  end

  def update
    if @link.update link_params
      render partial: 'domain_links/link', locals: { link: @link, domain: @link.left }
    else
      render json: { success: false }, status: 500
    end
  end

  def destroy
    if @link.destroy
      redirect_back fallback_location: root_path
    else
      flash[:danger] = 'Failed to remove link.'
      redirect_back fallback_location: @link.left
    end
  end

  private

  def link_params
    params.require(:domain_link).permit(:left_id, :right_id, :comments, :link_type)
  end

  def set_link
    @link = DomainLink.find params[:id]
  end
end


text:
# frozen_string_literal: true

class AbuseContactsController < ApplicationController
  before_action :verify_core, except: %i[index show]
  before_action :set_contact, except: %i[index create]
  before_action :verify_admin, only: [:destroy]

  def index
    @contacts = AbuseContact.all.order(:name).paginate(page: params[:page], per_page: 100)
  end

  def create
    @contact = AbuseContact.new contact_params
    if @contact.save
      flash[:success] = 'Created contact.'
      flash[:abuse_contact_id] = @contact.id
      redirect_back fallback_location: abuse_contact_path(@contact)
    else
      flash[:danger] = 'Failed to create contact.'
      redirect_back fallback_location: abuse_contacts_path
    end
  end

  def show
    @reports = @contact.reports.includes(:status, :contact, :user)
  end

  def edit; end

  def update
    if @contact.update contact_params
      flash[:success] = 'Contact updated.'
      redirect_to abuse_contact_path(@contact)
    else
      flash[:danger] = 'Failed to update contact.'
      render :edit
    end
  end

  def destroy
    if @contact.destroy
      flash[:success] = 'Removed contact.'
      redirect_to abuse_contacts_path
    else
      flash[:danger] = 'Failed to remove contct.'
      redirect_to abuse_contact_path(@contact)
    end
  end

  private

  def set_contact
    @contact = AbuseContact.find params[:id]
  end

  def contact_params
    params.require(:abuse_contact).permit(:name, :email, :link, :details)
  end
end


text:
# frozen_string_literal: true

class StackExchangeUsersController < ApplicationController
  before_action :authenticate_user!, only: [:update_data]
  before_action :verify_at_least_one_diamond, only: [:dead]
  before_action :set_stack_exchange_user, only: [:show]

  def index
    @users = StackExchangeUser.joins(:feedbacks)
                              .where('still_alive = true')
                              .where('stack_exchange_users.site_id = 1')
                              .where('feedbacks.feedback_type LIKE \'%t%\'')
                              .includes(:site)
                              .includes(:posts)
                              .group(:user_id)
                              .order(Arel.sql('created_at DESC'))
                              .limit(100)
  end

  def show
    @posts = @user.posts.includes_for_post_row.paginate(page: params[:page], per_page: 20)
  end

  def on_site
    @site = Site.find params[:site]
    @users = StackExchangeUser.joins(:feedbacks).where(site: @site, still_alive: true)
                              .where("feedbacks.feedback_type LIKE '%t%'").group(Arel.sql('stack_exchange_users.id'))
                              .order('(stack_exchange_users.question_count + stack_exchange_users.answer_count) DESC'\
                                     ', stack_exchange_users.reputation DESC')
                              .paginate(page: params[:page], per_page: 100)
  end

  def sites
    @sites = Site.all
  end

  def dead
    @user = StackExchangeUser.find params[:id]

    if @user.update(still_alive: false)
      render plain: 'ok', status: :ok
    else
      render plain: 'fail', status: :conflict
    end
  end

  def update_data
    site = Site.find params[:site]
    api_site_param = site.site_url.split('/')[-1].split('.')[0]
    Thread.new do
      live_ids = []
      StackExchangeUser.where(site: site, still_alive: true).in_groups_of(100).each do |group|
        ids = group.compact.map(&:user_id).join(';')
        filter = '!40D.p)TeT8rA79vLR'
        uri = "https://api.stackexchange.com/2.2/users/#{ids}?site=#{api_site_param}&key=#{AppConfig['stack_exchange']['key']}&filter=#{filter}"

        response = HTTParty.get(uri)
        jsn = response.parsed_response
        live_ids += jsn['items'].map { |u| u['user_id'] }
        sleep(jsn['backoff']) if jsn['backoff']
      end

      StackExchangeUser.where(site: site).where.not(user_id: live_ids).update_all(still_alive: false)
      site.update(last_users_update: DateTime.now)
    end

    flash[:info] = 'Data updates have been queued; check back in a few minutes.'
    redirect_to url_for(controller: :stack_exchange_users, action: :on_site, site: params[:site])
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_stack_exchange_user
    @user = StackExchangeUser.joins(:site).select(Arel.sql('stack_exchange_users.*, sites.site_logo')).find(params[:id])
  rescue
    @user = StackExchangeUser.find(params[:id])
  end
end


text:
# frozen_string_literal: true

require 'open-uri'
include AuthenticationHelper

class AuthenticationController < ApplicationController
  before_action :authenticate_user!, except: [:login_redirect_target]
  before_action :verify_admin, only: %i[invalidate_tokens send_invalidations]

  def status
    @config = AppConfig['stack_exchange']
  end

  def redirect_target
    token = access_token_from_code(params[:code])
    access_token_info = info_for_access_token(token)

    current_user.stack_exchange_account_id = access_token_info['account_id']
    current_user.update_chat_ids

    # temporarily disable SQL logging. http://stackoverflow.com/a/7760140/1849664
    old_logger = ActiveRecord::Base.logger
    ActiveRecord::Base.logger = nil

    current_user.save!

    ActiveRecord::Base.logger = old_logger

    if current_user.write_authenticated
      u = current_user
      Thread.new do
        # Do this in the background to keep the page load fast.
        u.update_moderator_sites
      end
    end

    flash[:success] = 'Successfully registered token'

    if current_user.write_authenticated && !current_user.flags_enabled
      redirect_to ocs_path
    else
      redirect_to authentication_status_path
    end
  end

  def login_redirect_target
    if user_signed_in?
      flash[:danger] = "You're already signed in."
      redirect_to(root_path) && return
    end

    token = access_token_from_code(params[:code], AppConfig['stack_exchange']['login_redirect_uri'])
    access_token_info = info_for_access_token(token)

    user = User.find_by(stack_exchange_account_id: access_token_info['account_id'])

    if user.present?
      flash[:success] = "Successfully logged in as #{user.username}"
    elsif !SiteSetting['registration_enabled']
      flash[:warning] = 'Registration is currently disabled.'
      redirect_to root_path
      return
    elsif Rails.cache.read("deleted_user_#{access_token_info['account_id']}").present?
      flash[:warning] = 'You may not recreate your account yet as you have recently deleted it.'
      redirect_to root_path
      return
    else
      user = User.new(stack_exchange_account_id: access_token_info['account_id'],
                      email: "#{access_token_info['account_id']}@se-oauth.metasmoke")

      user.username = user.get_username(token)

      user.password = user.password_confirmation = SecureRandom.hex

      user.save!

      Thread.new do
        # Do this in the background to keep the page load fast.
        user.update_chat_ids
        user.save!
      end

      flash[:success] = "New account created for #{user.username}. Have fun!"
    end
    sign_in_and_redirect user
  end

  def invalidate_tokens
    @users = User.all.where(write_authenticated: true)
  end

  def send_invalidations
    Thread.new do
      User.where(id: params[:users]).each do |user|
        HTTParty.post("#{AppConfig['token_store']['host']}/invalidate_tokens",
                      body: { account_id: user.stack_exchange_account_id }.to_json,
                      headers: { 'X-API-Key': AppConfig['token_store']['key'], 'Content-Type' => 'application/json' })
        user.update(write_authenticated: false)
      end
    end
    flash[:info] = 'Token invalidations queued.'
    redirect_to url_for(controller: :authentication, action: :invalidate_tokens)
  end
end


text:
# frozen_string_literal: true

class APIKeysController < ApplicationController
  before_action :authenticate_user!
  before_action :set_key, except: %i[index new create mine]
  before_action :verify_admin, except: %i[owner_edit owner_update owner_revoke mine]
  before_action :verify_ownership, only: %i[owner_edit owner_update owner_revoke]

  def index
    @keys = APIKey.all
  end

  def revoke_write_tokens
    if APIToken.where(api_key: @key).destroy_all
      flash[:success] = "Successfully removed all write tokens belonging to #{@key.app_name}."
    else
      flash[:danger] = 'Failed to revoke all API write tokens - tokens need to be removed manually.'
    end
    redirect_to url_for(controller: :api_keys, action: :index)
  end

  def new
    @key = APIKey.new
    @key.key = SecureRandom.hex 32
  end

  def create
    @key = APIKey.new(key_params)
    @key.save!
    flash[:success] = "Successfully registered API key #{@key.key}"
    redirect_to :admin_new_key
  end

  def edit; end

  def update
    if @key.update key_params
      flash[:success] = 'Successfully updated.'
      redirect_to url_for(controller: :api_keys, action: :index)
    else
      flash[:danger] = 'Failed to update.'
      render :edit
    end
  end

  def mine
    @keys = APIKey.where(user: current_user)
  end

  def owner_edit; end

  def owner_update
    if @key.update owner_edit_params
      flash[:success] = 'Successfully updated your API key.'
      redirect_to url_for(controller: :api_keys, action: :mine)
    else
      flash[:danger] = "Can't save your API key - contact an admin."
      render :edit
    end
  end

  def owner_revoke
    if APIToken.where(api_key: @key).destroy_all
      flash[:success] = "Removed all active write tokens for your app #{@key.app_name}."
    else
      flash[:danger] = 'Failed to remove active write tokens. Contact an admin.'
    end
    redirect_to url_for(controller: :api_keys, action: :mine)
  end

  def update_trust
    if @key.update(is_trusted: params[:trusted] == 'true')
      render plain: 'OK'
    else
      render plain: 'nope'
    end
  end

  private

  def key_params
    params.require(:api_key).permit(:key, :app_name, :user_id, :github_link, :key_type)
  end

  def set_key
    @key = APIKey.find params[:id]
  end

  def verify_ownership
    raise ActionController::RoutingError, 'Not Found' unless current_user == @key.user || current_user.is_admin
  end

  def owner_edit_params
    params.require(:api_key).permit(:app_name, :github_link)
  end
end