class QuickML::Processor

Constants

UNSUBSCRIBE_RE
UNSUBSCRIBE_THRESHOLD

Public Class Methods

new(config, mail) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 23
def initialize (config, mail)
  @config = config
  @mail = mail
  @logger = @config.logger
  @catalog = @config.catalog
  if mail.multipart?
    sub_mail = Mail.new
    sub_mail.read(mail.parts.first)
    @message_charset = sub_mail.charset
  else
    @message_charset = mail.charset
  end

  # FIXME: too ad-hoc
  @rejection_ignore_list = %w(info ajax study test)
  if $test
    @rejection_ignore_list = []
    if $test_rejection_ignore_list
      @rejection_ignore_list = $test_rejection_ignore_list
    end
  end
end

Private Class Methods

unsubscribe_requested?(body) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 128
def self.unsubscribe_requested?(body)
  return true if body.empty?
  return true if Mail.empty_body?(body)
  return false if UNSUBSCRIBE_THRESHOLD <= body.length
  return true if UNSUBSCRIBE_RE.match(body.tosjis)
  return false
end

Public Instance Methods

process() click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 46
def process
  mail_log
  if @mail.looping?
    @logger.log "Looping Mail: from #{@mail.from}"
    return
  end

  @mail.recipients.each {|recipient|
    process_recipient(recipient)
  }
end

Private Instance Methods

acceptable_submission?(ml) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 378
def acceptable_submission? (ml)
  ml.newly_created? ||
    ml.active_members_include?(@mail.from) ||
    ml.former_members_include?(@mail.from) ||
    sender_knows_an_active_member?(ml)
end
add_member(ml, address) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 316
def add_member (ml, address)
  begin
    ml.add_member(address)
  rescue TooManyMembers
    @unadded_addresses.push(address)
  end
end
confirmation_required?(ml) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 385
def confirmation_required? (ml)
  @config.confirm_ml_creation and ml.newly_created?
end
content_type() click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 180
def content_type
  return Mail.content_type(@config.content_type, @message_charset)
end
mail_log() click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 60
def mail_log
  @logger.vlog "MAIL FROM:<#{@mail.mail_from}>"
  @mail.recipients.each {|recipient|
    @logger.vlog "RCPT TO:<#{recipient}>"
  }
  @logger.vlog 'From: ' + @mail.from
  @logger.vlog 'Cc: ' + @mail.collect_cc.join(', ')
  @logger.vlog 'bare From: ' + @mail['From']
  @logger.vlog 'bare Cc: ' + @mail['Cc']
end
ml_address_in_to?(ml) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 324
def ml_address_in_to? (ml)
  return @mail.collect_to.find {|address|
    address == ml.address
  }
end
process_recipient(recipient) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 71
def process_recipient (recipient)
  mladdress = recipient

  # Error mail handling.
  if to_return_address?(mladdress)
    handler = ErrorMailHandler.new(@config, @message_charset)
    handler.handle(@mail)
    return
  end

  # Confirm to create a new mailing list.
  if @config.confirm_ml_creation && to_confirmation_address?(mladdress)
    validate_confirmation(mladdress)
    return
  end

  begin
    ServerMemory.ml_mutex(@config, mladdress).synchronize {
      ml = Group.new(@config, mladdress, @mail.from, @message_charset)
      @message_charset ||= ml.charset

      #qp @mail.body
      if Processor.unsubscribe_requested?(@mail.body)
        unsubscribe(ml)
        return
      end

      submit(ml)
    }

  rescue InvalidMLName
    report_invalid_mladdress(mladdress)
  end
end
report_invalid_mladdress(mladdress) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 161
def report_invalid_mladdress (mladdress)
  mail = {
    :mail_from => '', 
    :recipient => @mail.from,
    :header => [
      ['To',       @mail.from],
      ['From',     @config.ml_postmaster],
      ['Subject',
        Mail.encode_field(_("[QuickML] Error: %s", @mail['Subject']))],
      ['Content-Type', content_type]
    ],
    :body => _("Invalid mailing list name: <%s>\n", mladdress) +
    _("You can only use 0-9, a-z, A-Z,  `-' for mailing list name\n") +
    generate_footer,
  }
  Sendmail.send_mail(@config.smtp_host, @config.smtp_port, @logger, mail)
  @logger.log "Invalid ML Address: #{mladdress}"
end
report_rejection(ml) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 188
    def report_rejection (ml)
      # FIXME: too ad-hoc
      if @rejection_ignore_list.include?(ml.name)
        @logger.log "[#{ml.name}]: Reject quietly: #{@mail.from}"

        # do nothing
        return 
      end

      header = []
      subject = Mail.encode_field(_("[QuickML] Error: %s", @mail['Subject']))
      header.push(['To',        @mail.from],
                  ['From',    ml.address],
                  ['Subject', subject])

      body =  _("You are not a member of the mailing list:\n<%s>\n",
                ml.address)
      body << "\n"
      body <<  _("Did you send a mail with a different address from the address registered in the mailing list?\n")
      body <<  _("Please check your 'From:' address.\n")
      body << generate_footer
      body << "\n"

      body << _("----- Original Message -----\n")
      orig_subject = codeconv(Mail.decode_subject(@mail['Subject']))
      body << "Subject: #{orig_subject}\n"
      body << "To: #{@mail['To']}\n"
      body << "From: #{@mail['From']}\n"
      body << "Date: #{@mail['Date']}\n"
      body << "\n"

=begin
      if @mail.multipart?
        ['Content-Type', 'Mime-Version', 
          'Content-Transfer-Encoding'].each {|key|
          header.push([key, @mail[key]]) unless @mail[key].empty?
        }
        sub_mail = Mail.new
        parts = @mail.parts
        sub_mail.read(parts.first)
        body << sub_mail.body
        sub_mail.body = body
        parts[0] = sub_mail.to_s
        body = Mail.join_parts(parts, @mail.boundary)
      else
        unless @mail['Content-Type'].empty?
          header.push(['Content-Type', @mail['Content-Type']]) 
        end
        body << @mail.body
      end
=end

      body <<  _("The original body is omitted to avoid spam trouble.\n")

      mail = {
        :mail_from => '', 
        :recipient => @mail.from,
        :header => header,
        :body => body,
      }
      Sendmail.send_mail(@config.smtp_host, @config.smtp_port, @logger, mail)
      @logger.log "[#{ml.name}]: Reject: #{@mail.from}"
    end
report_too_many_members(ml, unadded_addresses) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 281
def report_too_many_members (ml, unadded_addresses)
  header = [
    ['To', @mail.from],
    ['From',       ml.address],
    ['Subject',
      Mail.encode_field(_("[QuickML] Error: %s", @mail['Subject']))],
    ['Content-Type', content_type]
  ]
  body =  _("The following addresses cannot be added because <%s> mailing list reaches the maximum number of members (%d persons)\n\n",
            ml.address,
            ml.get_max_members)
  unadded_addresses.each {|address|
    body << sprintf("<%s>\n", address)
  }

  body << generate_footer

  mail = {
    :mail_from => '', 
    :recipient => @mail.from,
    :header => header,
    :body => body,
  }
  Sendmail(@config.smtp_host, @config.smtp_port, @logger, mail)

  str = unadded_addresses.join(',')
  @logger.log "[#{ml.name}]: Too Many Members: #{str}"
end
report_unsubscription(ml, member, requested_by = nil) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 252
def report_unsubscription (ml, member, requested_by = nil)
  header = []
  subject = Mail.encode_field(_("[%s] Unsubscribe: %s",
                                ml.name, ml.address))
  header.push(['To',        member],
              ['From',    ml.address],
              ['Subject', subject],
              ['Content-Type', content_type])

  if requested_by
    body =  _("You are removed from the mailing list:\n<%s>\n",
              ml.address)
    body << _("by the request of <%s>.\n", requested_by)
  else
    body = _("You have unsubscribed from the mailing list:\n<%s>.\n", 
             ml.address)
  end
  body << generate_footer

  mail = {
    :mail_from => '', 
    :recipient => member,
    :header => header,
    :body => body,
  }
  Sendmail.send_mail(@config.smtp_host, @config.smtp_port, @logger, mail)
  @logger.log "[#{ml.name}]: Unsubscribe: #{member}"
end
sender_knows_an_active_member?(ml) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 310
def sender_knows_an_active_member? (ml)
  return @mail.collect_cc.find {|address|
    ml.active_members_include?(address)
  }
end
submit(ml) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 136
def submit (ml)
  if Group.exclude?(@mail.from, @config.ml_domain)
    @logger.log "Invalid From Address: #{@mail.from}"
    return
  end

  if ml.forward? 
    @logger.log "Forward Address: #{ml.address}"
    ml.submit(@mail)
    return
  end

  if confirmation_required?(ml)
    ml.prepare_confirmation(@mail)
    return
  end

  if acceptable_submission?(ml)
    submit_article(ml)
    return
  end

  report_rejection(ml)
end
submit_article(ml) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 330
def submit_article (ml)
  @unadded_addresses = []

  if ml_address_in_to?(ml)
    add_member(ml, @mail.from)
    @mail.collect_cc.each {|address| 
      add_member(ml, address)
    }
  end

  if ! @unadded_addresses.empty?
    report_too_many_members(ml, @unadded_addresses)
  end

  ml.submit(@mail)
end
to_confirmation_address?(address) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 111
def to_confirmation_address? (address)
  return /\Aconfirm\+/.match(address)
end
to_return_address?(recipient) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 106
def to_return_address? (recipient)
  # "return=" for XVERP, 'return@' for without XVERP.
  return /^[^=]*=return[=@]/ =~ recipient
end
unsubscribe(ml) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 369
def unsubscribe (ml)
  cc = @mail.collect_cc
  if cc.empty?
    unsubscribe_self(ml)
  else
    unsubscribe_other(ml, cc)
  end
end
unsubscribe_other(ml, cc) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 356
def unsubscribe_other (ml, cc)
  if ml.active_members_include?(@mail.from)
    cc.each {|other|
      if ml.active_members_include?(other)
        ml.remove_member(other) 
        report_unsubscription(ml, other, @mail.from)
      end
    }
  else
    @logger.vlog 'rejected'
  end
end
unsubscribe_self(ml) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 347
def unsubscribe_self (ml)
  if ml.active_members_include?(@mail.from)
    ml.remove_member(@mail.from)
    report_unsubscription(ml, @mail.from)
  else
    report_rejection(ml)
  end
end
validate_confirmation(confirmation_address) click to toggle source
# File vendor/qwik/lib/qwik/ml-processor.rb, line 115
def validate_confirmation (confirmation_address)
  m = /\Aconfirm\+(\d+)\+(.*)/.match(confirmation_address)
  return if m.nil?
  time = m[1]
  mladdress = m[2]
  ml = Group.new(@config, mladdress)
  if ml.validate_confirmation(time)
    ml.accept_confirmation
  end
end