* Added .deepsource.toml * Removed bad use of `alias` * Fixed operand order in the binary expression * Prefixed unused method arguments with an underscore * Replaced the old OpenSSL algorithmic constants with the newer strings initializers. * Removed unnecessary UTF-8 encoding comment
		
			
				
	
	
		
			138 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			138 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
require 'concurrent'
 | 
						|
require_relative '../../config/boot'
 | 
						|
require_relative '../../config/environment'
 | 
						|
require_relative 'cli_helper'
 | 
						|
 | 
						|
module Mastodon
 | 
						|
  class EmailDomainBlocksCLI < Thor
 | 
						|
    include CLIHelper
 | 
						|
 | 
						|
    def self.exit_on_failure?
 | 
						|
      true
 | 
						|
    end
 | 
						|
 | 
						|
    desc 'list', 'List blocked e-mail domains'
 | 
						|
    def list
 | 
						|
      EmailDomainBlock.where(parent_id: nil).order(id: 'DESC').find_each do |entry|
 | 
						|
        say(entry.domain.to_s, :white)
 | 
						|
 | 
						|
        EmailDomainBlock.where(parent_id: entry.id).order(id: 'DESC').find_each do |child|
 | 
						|
          say("  #{child.domain}", :cyan)
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    option :with_dns_records, type: :boolean
 | 
						|
    desc 'add DOMAIN...', 'Block e-mail domain(s)'
 | 
						|
    long_desc <<-LONG_DESC
 | 
						|
      Blocking an e-mail domain prevents users from signing up
 | 
						|
      with e-mail addresses from that domain. You can provide one or
 | 
						|
      multiple domains to the command.
 | 
						|
 | 
						|
      When the --with-dns-records option is given, an attempt to resolve the
 | 
						|
      given domains' DNS records will be made and the results (A, AAAA and MX) will
 | 
						|
      also be blocked. This can be helpful if you are blocking an e-mail server that
 | 
						|
      has many different domains pointing to it as it allows you to essentially block
 | 
						|
      it at the root.
 | 
						|
    LONG_DESC
 | 
						|
    def add(*domains)
 | 
						|
      if domains.empty?
 | 
						|
        say('No domain(s) given', :red)
 | 
						|
        exit(1)
 | 
						|
      end
 | 
						|
 | 
						|
      skipped = 0
 | 
						|
      processed = 0
 | 
						|
 | 
						|
      domains.each do |domain|
 | 
						|
        if EmailDomainBlock.where(domain: domain).exists?
 | 
						|
          say("#{domain} is already blocked.", :yellow)
 | 
						|
          skipped += 1
 | 
						|
          next
 | 
						|
        end
 | 
						|
 | 
						|
        email_domain_block = EmailDomainBlock.new(domain: domain, with_dns_records: options[:with_dns_records] || false)
 | 
						|
        email_domain_block.save!
 | 
						|
        processed += 1
 | 
						|
 | 
						|
        next unless email_domain_block.with_dns_records?
 | 
						|
 | 
						|
        hostnames = []
 | 
						|
        ips       = []
 | 
						|
 | 
						|
        Resolv::DNS.open do |dns|
 | 
						|
          dns.timeouts = 5
 | 
						|
          hostnames = dns.getresources(email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s }
 | 
						|
 | 
						|
          ([email_domain_block.domain] + hostnames).uniq.each do |hostname|
 | 
						|
            ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::A).to_a.map { |e| e.address.to_s })
 | 
						|
            ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::AAAA).to_a.map { |e| e.address.to_s })
 | 
						|
          end
 | 
						|
        end
 | 
						|
 | 
						|
        (hostnames + ips).uniq.each do |hostname|
 | 
						|
          another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: email_domain_block)
 | 
						|
 | 
						|
          if EmailDomainBlock.where(domain: hostname).exists?
 | 
						|
            say("#{hostname} is already blocked.", :yellow)
 | 
						|
            skipped += 1
 | 
						|
            next
 | 
						|
          end
 | 
						|
 | 
						|
          another_email_domain_block.save!
 | 
						|
          processed += 1
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      say("Added #{processed}, skipped #{skipped}", color(processed, 0))
 | 
						|
    end
 | 
						|
 | 
						|
    desc 'remove DOMAIN...', 'Remove e-mail domain blocks'
 | 
						|
    def remove(*domains)
 | 
						|
      if domains.empty?
 | 
						|
        say('No domain(s) given', :red)
 | 
						|
        exit(1)
 | 
						|
      end
 | 
						|
 | 
						|
      skipped = 0
 | 
						|
      processed = 0
 | 
						|
      failed = 0
 | 
						|
 | 
						|
      domains.each do |domain|
 | 
						|
        entry = EmailDomainBlock.find_by(domain: domain)
 | 
						|
 | 
						|
        if entry.nil?
 | 
						|
          say("#{domain} is not yet blocked.", :yellow)
 | 
						|
          skipped += 1
 | 
						|
          next
 | 
						|
        end
 | 
						|
 | 
						|
        children_count = EmailDomainBlock.where(parent_id: entry.id).count
 | 
						|
        result = entry.destroy
 | 
						|
 | 
						|
        if result
 | 
						|
          processed += children_count + 1
 | 
						|
        else
 | 
						|
          say("#{domain} could not be unblocked.", :red)
 | 
						|
          failed += 1
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      say("Removed #{processed}, skipped #{skipped}, failed #{failed}", color(processed, failed))
 | 
						|
    end
 | 
						|
 | 
						|
    private
 | 
						|
 | 
						|
    def color(processed, failed)
 | 
						|
      if !processed.zero? && failed.zero?
 | 
						|
        :green
 | 
						|
      elsif failed.zero?
 | 
						|
        :yellow
 | 
						|
      else
 | 
						|
        :red
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |