class Schleuder::Runner

Public Instance Methods

run(msg, recipient) click to toggle source
# File lib/schleuder/runner.rb, line 3
def run(msg, recipient)
  error = setup_list(recipient)
  return error if error

  logger.info 'Parsing incoming email.'

  # is it valid utf-8?
  msg_scrubbed = false
  unless msg.valid_encoding?
    logger.warn 'Converting message due to invalid characters'
    detection = CharlockHolmes::EncodingDetector.detect(msg)
    begin
      msg = CharlockHolmes::Converter.convert(msg, detection[:encoding], 'UTF-8')
    rescue ArgumentError
      # it looks like even icu wasn't able to convert
      # so we scrub the invalid characters to be able to
      # at least parse the message somehow. Though this might
      # result in data loss.
      logger.warn 'Scrubbing message due to invalid characters'
      msg = msg.scrub
      msg_scrubbed = true
    end
  end

  @mail = Mail.create_message_to_list(msg, recipient, list)

  if msg_scrubbed
    @mail.add_pseudoheader(:note, I18n.t('pseudoheaders.scrubbed_message'))
  end

  error = run_filters('pre')
  if error
    if bounce?(error, @mail)
      return error
    else
      return nil
    end
  end

  begin
    # This decrypts, verifies, etc.
    @mail = @mail.setup

  rescue GPGME::Error::BadPassphrase,
         GPGME::Error::DecryptFailed,
         GPGME::Error::NoData,
         GPGME::Error::NoSecretKey,
         GPGME::Error::Canceled

    logger.warn 'Decryption of incoming message failed.'
    return Errors::DecryptionFailed.new(list)
  end

  error = run_filters('post')
  if error
    if bounce?(error, @mail)
      return error
    else
      return nil
    end
  end

  if ! @mail.was_validly_signed?
    logger.debug 'Message was not validly signed, adding subject_prefix_in'
    @mail.add_subject_prefix_in!
  end

  if ! @mail.was_encrypted?
    logger.debug 'Message was not encrypted, skipping keyword-handlers'
  elsif @mail.was_validly_signed?
    # run KeywordHandlers
    logger.debug 'Message was encrypted and validly signed'
    KeywordHandlersRunner.run(type: :list, list: list, mail: @mail).compact
  end

  # Don't send empty messages over the list.
  if @mail.empty?
    logger.info 'Message found empty, not sending it to list.'
    return Errors::MessageEmpty.new(@list)
  end

  logger.debug 'Adding subject_prefix'
  @mail.add_subject_prefix!

  # Subscriptions
  logger.debug 'Creating clean copy of message'
  copy = @mail.clean_copy(list.headers_to_meta.any?)
  list.send_to_subscriptions(copy, @mail)
  nil
end

Private Instance Methods

bounce?(response, mail) click to toggle source
# File lib/schleuder/runner.rb, line 106
def bounce?(response, mail)
  if list.bounces_drop_all
    list.logger.debug 'Dropping bounce as configurated'
    notify_admins(I18n.t('.bounces_drop_all'), mail.original_message)
    return false
  end

  list.bounces_drop_on_headers.each do |key, value|
    if mail[key].to_s.match(/#{value}/i)
      list.logger.debug "Incoming message header key '#{key}' matches value '#{value}': dropping the bounce."
      notify_admins(I18n.t('.bounces_drop_on_headers', key: key, value: value), mail.original_message)
      return false
    end
  end

  list.logger.debug 'Bouncing message'
  notify_admins("#{I18n.t('.bounces_notify_admins')}\n\n#{response}", mail.original_message)
  true
end
filters_runner(filter_type) click to toggle source
# File lib/schleuder/runner.rb, line 130
def filters_runner(filter_type)
  if filter_type == 'pre'
    filters_runner_pre_decryption
  else
    filters_runner_post_decryption
  end
end
filters_runner_post_decryption() click to toggle source
# File lib/schleuder/runner.rb, line 142
def filters_runner_post_decryption
  @filters_runner_post_decryption ||= Filters::Runner.new(list, 'post')
end
filters_runner_pre_decryption() click to toggle source
# File lib/schleuder/runner.rb, line 138
def filters_runner_pre_decryption
  @filters_runner_pre_decryption ||= Filters::Runner.new(list, 'pre')
end
list() click to toggle source
# File lib/schleuder/runner.rb, line 96
def list
  @list
end
log_and_return(error, reveal_error=false) click to toggle source
# File lib/schleuder/runner.rb, line 150
def log_and_return(error, reveal_error=false)
  Schleuder.logger.error(error)
  if reveal_error
    error
  else
    # Return an unrevealing error, the sender and all bystanders don't need to know these details.
    Errors::FatalError.new
  end
end
logger() click to toggle source
# File lib/schleuder/runner.rb, line 146
def logger
  list.present? && list.logger || Schleuder.logger
end
notify_admins(reason, original_message) click to toggle source
# File lib/schleuder/runner.rb, line 100
def notify_admins(reason, original_message)
  if list.bounces_notify_admins
    list.logger.notify_admin reason, original_message, I18n.t('notice')
  end
end
run_filters(filter_type) click to toggle source
# File lib/schleuder/runner.rb, line 126
def run_filters(filter_type)
  filters_runner(filter_type).run(@mail)
end
setup_list(recipient) click to toggle source
# File lib/schleuder/runner.rb, line 160
def setup_list(recipient)
  return @list if @list

  logger.info "Loading list '#{recipient}'"
  if ! @list = List.by_recipient(recipient)
    return log_and_return(Errors::ListNotFound.new(recipient), true)
  end

  # Check necessary permissions of crucial files.
  if ! File.exist?(@list.listdir)
    return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_existing))
  elsif ! File.directory?(@list.listdir)
    return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_a_directory))
  elsif ! File.readable?(@list.listdir)
    return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_readable))
  elsif ! File.writable?(@list.listdir)
    return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_writable))
  else
    if File.exist?(@list.logfile) && ! File.writable?(@list.logfile)
      return log_and_return(Errors::ListdirProblem.new(@list.logfile, :not_writable))
    end
  end

  # Check basic sanity of list.
  %w[fingerprint key secret_key admins].each do |attrib|
    if @list.send(attrib).blank?
      return log_and_return(Errors::ListPropertyMissing.new(@list.listdir, attrib))
    end
  end

  # Set locale
  if I18n.available_locales.include?(@list.language.to_sym)
    I18n.locale = @list.language.to_sym
  end

  # This cannot be put in List, as Mail wouldn't know it then.
  logger.debug "Setting GNUPGHOME to #{@list.listdir}"
  ENV['GNUPGHOME'] = @list.listdir
  nil
end