class Smailr::Cli

Public Instance Methods

ask_password() click to toggle source

Run an interactive cli dialog to enter and confirm a password.

Returns a string containing the entered password if password and confirmation match.

# File lib/smailr/cli.rb, line 23
def ask_password
    min_password_length = Smailr.config["password_policy"]["length"]

    password = ask("Password: ") { |q| q.echo = "*" }
    confirm  = ask("Confirm: ")  { |q| q.echo = "*" }

    if password != confirm
        say("Mismatch; try again.")
        ask_password
    end

    if password.length < min_password_length.to_i
        say("Too short; try again.")
        ask_password
    end

    password
end
determine_object(string) click to toggle source

Determine whether we the passed string is a domain or a mail address.

Returns either :domain or :address

# File lib/smailr/cli.rb, line 14
def determine_object(string)
    return :domain  if string =~ /^[^@][A-Z0-9.-]+\.[A-Z]{2,6}$/i
    return :address if string =~ /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$/i
end
run() click to toggle source

Initialize the Cli

# File lib/smailr/cli.rb, line 43
def run
  program :description, 'smailr - Virtual Mail Hosting Management CLI'
  program :version, Smailr::VERSION

  ### Commands

  command :add do |c|
    c.syntax = 'smailr add domain | mailbox | alias [options]'
    c.summary = 'Add a new domain, mailbox or alias to the mail system.'
    c.example 'Add a domain',  'smailr add example.com'
    c.example 'Add a mailbox', 'smailr add user@example.com'
    c.example 'Add an alias',  'smailr add alias@localdom.com --alias user@example.com,user1@example.com'
    c.example 'Setup DKIM for a domain', 'smailr add ono.at --dkim'
    c.option  '--alias DESTINATION', String, 'Specify the alias destination.'
    c.option  '--password PASSWORD', String, 'The password for a new mailbox. If you omit this option, it prompts for one.'
    c.option  '--dkim SELECTOR',     String, 'Add a DKIM Key with the specified selector for domain.'
    c.action do |args, options|
      address = args[0]
      type    = determine_object(address)

      case type
        when :domain
          if options.dkim
            selector = options.dkim
            key = Smailr::Dkim.add(address, selector)

            puts "public-key " + key.split("\n").slice(1..-2).join
          else
            Smailr::Domain.add(address)
          end

        when :address
          if options.alias
            source       = args[0]
            destinations = options.alias.split(',')
            Smailr::Alias.add(source, destinations)
          else
            options.password ||= ask_password
            Smailr::Mailbox.add(address, options.password)
          end

      end
    end
  end

  command :ls do |c|
    c.syntax  = 'smailr ls [domain]'
    c.summary = 'List domains or mailboxes and aliases of a specific domain.'
    c.action do |args, options|
      case args[0]
      when /^[^@][A-Z0-9.-]+\.[A-Z]{2,6}$/i then
        domain = Smailr::Model::Domain[:fqdn => args[0]]
        domain.mailboxes.each do |mbox|
          puts "m: #{mbox.localpart}@#{args[0]}"
        end
        domain.aliases.each do |aliass|
          puts "a: #{aliass.localpart}@#{args[0]} > #{aliass.dstlocalpart}@#{aliass.dstdomain}"
        end
      when nil
        domains = Smailr::DB[:domains]
        domains.all.each do |d|
          domain = Smailr::Model::Domain[:fqdn => d[:fqdn]]
          puts d[:fqdn]
        end
      else
        error "You can either list a domains or a domains addresses."
        exit 1
      end
    end
  end

  command :rm do |c|
    c.syntax  = 'smailr rm domain | mailbox [options]'
    c.summary = 'Remove a domain, mailbox or alias known to the mail system.'
    c.example 'Remove a domain', 'smailr rm example.com'
    c.option '--force', 'Force the operation, do not ask for confirmation.'
    c.option '--dkim SELECTOR',  String, 'Remove a dkim key.' 
    c.option '--alias DESTINATION', String, 'Specify the destination you want to remove from the alias.'
    c.action do |args, options|
      address = args[0]
      type    = determine_object(address)
      case type
      when :domain
        if options.dkim
          selector = options.dkim
          Smailr::Dkim.rm(address, selector)
        else
          Smailr::Domain.rm(address, options.force)
        end

      when :address
        if options.alias
          source       = args[0]
          destinations = options.alias.split(',')

          Smailr::Alias.rm(source, destinations)
        else
          Smailr::Mailbox.rm(address, options)
        end
      end
    end
  end

  command :passwd do |c|
    c.syntax  = 'smailr passwd mailbox'
    c.summary = 'Update a users password.'
    c.action do |args,options|
      address  = args[0]
      password = ask_password
      Smailr::Mailbox.update_password(address, password)
    end
  end


  command :setup do |c|
    c.syntax  = 'smailr setup'
    c.summary = 'Install all required components on a mailserver'
    c.action do |args,options|
      Smailr::Setup.new.run
    end
  end


  command :migrate do |c|
    c.syntax  = 'smailr migrate [options]'
    c.summary = 'Create database and run migrations'
    c.option '--to VERSION', String, 'Migrate the database to a specifict version.'
    c.action do |args,options|
      require 'sequel/extensions/migration'
      raise "Database not configured" unless Smailr::DB

      if options.to.nil?
        if Sequel::Migrator.is_current?(Smailr::DB, Smailr.migrations_directory)
          puts "Database schema already up to date. Exiting"
          exit 0
        end

        puts "Running database migrations to latest version."
        Sequel::Migrator.apply(Smailr::DB, Smailr.migrations_directory)
      else
        puts "Running database migrations to version: #{options.to}"
        Sequel::Migrator.apply(Smailr::DB, Smailr.migrations_directory, options.to.to_i)
      end
    end
  end


  command :mutt do |c|
    base = Smailr.config["mail_spool_path"]

    c.syntax      = "smailr mutt address"
    c.summary     = "View the mailbox of the specified address in mutt."
    c.description = "Open the mailbox of the specified address in mutt.\n\n    " +
      "Requires that mutt is installed and tries to find a suitable maildir in: " + base
    c.example       'Open test@example.com', 'smailr mutt test@example.com'
    c.action do |args,options|
      localpart, fqdn = args[0].split('@')

      mutt = `command -v mutt || { echo "Please install mutt first. Aborting." >&2; exit 1; }`
      if $?

        possibilities = [
          "#{base}/#{fqdn}/#{localpart}/Maildir",
        "#{base}/users/#{fqdn}/#{localpart}/Maildir",
        "#{base}/users/#{fqdn}/#{localpart}/.maildir",
        "#{base}/users/#{fqdn}/#{localpart}"
      ]

        possibilities.each do |path|
          if File.readable?(path)
            puts "Opening maildir #{path} with mutt."
            exec "MAIL=#{path} MAILDIR=#{path} #{mutt} -mMaildir"
          end
        end
      end
    end
  end

  command :verify do |c|
    c.syntax      = "smailr verify address"
    c.summary     = "Send out a test message to verify a domains configuration via verifier.port25.com"
    c.description = "A reply email will be sent back to you with an analysis of the message’s authentication" +
      "status.  The report will perform the following checks: SPF, SenderID, DomainKeys, DKIM " +
      "and Spamassassin.\n\n"
    c.example       'Verify test@example.com (report will be sent to test@example.com)', 'smailr verify test@example.com'
    c.example       'Verify test@example.com, send report to root@example.com', 'smailr verify test@example.com --report-to root@example.com'

    c.option        '-r DESTINATION', '--report-to DESTINATION', String, 'Send the report to the specified address instead.'

    c.action do |args,options|
      from = args[0]
      options.default :report_to => from

      dstlocalpart, dstfqdn = options.report_to.split('@')

      require 'socket'
      require 'date'
      require 'net/smtp'
      Net::SMTP.start('localhost', 25) do |smtp|
        to = "check-auth-#{dstlocalpart}=#{dstfqdn}@verifier.port25.com"

        message = [
          "From: #{from}",
          "To: #{to}",
          "Subject: Port25 Mail Verification Test",
            "Date: #{DateTime.now.strftime("%a, %d %b %Y %H:%M:%S %z")}",
          "",
            "This is a test message for the port25 mail verification test, it was",
            "sent from the following server: #{Socket.gethostname}."
        ].join("\r\n")

        smtp.send_message(message, from, to)
      end
    end
  end

  run!
end