class QuickML::Group

Attributes

address[R]
charset[R]
count[R]
name[R]
return_address[R]

Public Class Methods

exclude?(address, config_domain) click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 35
def self.exclude?(address, config_domain)
  name, domain = address.split('@')
  return true if domain.nil?
  return Mail.address_of_domain?(address, config_domain)
end
new(config, address, creator = nil, message_charset = nil) click to toggle source
initialize
# File vendor/qwik/lib/qwik/group.rb, line 37
def initialize (config, address, creator = nil, message_charset = nil)
  @config = config
  @address = address
  @name, domain = Group.get_name(@address)
  @return_address = Group.generate_return_address(@address,
                                                  @config.use_qmail_verp)

  # init_db
  @db = GroupDB.new(@config.sites_dir, @name)
  init_site         # initialize for GroupSite
  @db.set_site(@site)

  @added_members = []

  @logger = @config.logger
  @catalog = @config.catalog

  init_group_config
  init_members
  init_count
  init_charset

  @message_charset = message_charset || @charset
  @logger.log "[#{@name}]: New ML by #{creator}" if newly_created?
end

Private Class Methods

generate_return_address(address, use_qmail_verp = false) click to toggle source
# File vendor/qwik/lib/qwik/group.rb, line 98
def self.generate_return_address(address, use_qmail_verp = false)
  name, domain = Group.get_name(address)

  # e.g. <foo=return=@quickml.com-@[]>
  return "#{name}=return=@#{domain}-@[]" if use_qmail_verp

  # e.g. <foo=return@quickml.com>
  return "#{name}=return@#{domain}"
end
get_name(address) click to toggle source
# File vendor/qwik/lib/qwik/group.rb, line 86
def self.get_name(address)
  raise InvalidMLName if /@.*@/ =~ address
  name, host = address.split('@')
  raise InvalidMLName if ! valid_name?(name)
  return name, host
end
load_charset(db) click to toggle source
# File vendor/qwik/lib/qwik/group-cond.rb, line 43
def self.load_charset(db)
  return nil if ! db.exist?(:Charset)
  return parse_charset(db.get(:Charset))
end
load_count(db) click to toggle source
# File vendor/qwik/lib/qwik/group-cond.rb, line 27
def self.load_count(db)
  return 0 if ! db.exist?(:Count)
  content = db.get(:Count)
  return (content.to_a.first || '1').chomp.to_i
end
parse_charset(content) click to toggle source
# File vendor/qwik/lib/qwik/group-cond.rb, line 48
def self.parse_charset(content)
  return (content.to_a.first || '').chomp
end
valid_name?(name) click to toggle source
# File vendor/qwik/lib/qwik/group.rb, line 93
def self.valid_name? (name)
  # Do not allow '_' and '.' for the name.
  return /\A[0-9a-zA-Z-]+\z/ =~ name
end

Public Instance Methods

accept_confirmation() click to toggle source

ml-processor.rb:98: ml.accept_confirmation

# File vendor/qwik/lib/qwik/group-confirm.rb, line 37
def accept_confirmation
  # Add suspended members.
  waiting_members = (waiting_members_get || '').to_a.map {|line|
    line.chomp
  }
  waiting_members.each {|address|
    begin
      add_member(address)
    rescue TooManyMembers
    end
  }

  # Read suspended message.
  waiting_message = waiting_message_get || ''
  mail = Mail.create { waiting_message }

  submit(mail)      # Send it.

  waiting_members_delete
  waiting_message_delete
  @logger.log "[#{@name}]: Accept confirmation:  #{@address}"
end
active_members_include?(address) click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 16
def active_members_include?(address)
  return @members.active_include?(address)
end
add_error_member(address) click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 48
def add_error_member (address)
  return if ! @members.active_include?(address)
  prev_count = @members.error_count(address)
  count = @members.inc_error_count(address)
  if prev_count == count
    @logger.log "[#{@name}]: AddError: #{address} (not counted)"
  else
    @logger.log "[#{@name}]: AddError: #{address} #{count}"
  end
  @members.save

  if @group_config[:auto_unsubscribe_count] <= @members.error_count(address)
    remove_member(address)
    report_removed_member(address)
  end
end
add_member(address) click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 24
def add_member (address)
  if Group.exclude?(address, @config.ml_domain)
    @logger.vlog "Excluded: #{address}"
    return
  end
  return if @members.active_include?(address)       # Already included.
  @members.add_member(address)
  @added_members.push(address)
  @logger.log "[#{@name}]: Add: #{address}"
end
close() click to toggle source
# File vendor/qwik/lib/qwik/group.rb, line 73
def close
  @db.delete(:Members)
  @db.delete(:Count)
  @db.delete(:Alerted)
  @db.delete(:Charset)
  @db.delete(:Config)
  @db.delete(:WaitingMembers)
  @db.delete(:WaitingMessage)
  @logger.log("[#{@name}]: ML Closed")
end
close_dummy() click to toggle source
# File vendor/qwik/lib/qwik/group.rb, line 69
def close_dummy
  @logger.log("[#{@name}]: ML will be closed")
end
former_members_include?(address) click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 20
def former_members_include?(address)
  return @members.former_include?(address)
end
forward?() click to toggle source
# File vendor/qwik/lib/qwik/group-config.rb, line 16
def forward?
  return @group_config.forward?
end
get_max_members() click to toggle source
# File vendor/qwik/lib/qwik/group-config.rb, line 24
def get_max_members
  return @group_config[:max_members]
end
group_config_check_exist() click to toggle source
# File vendor/qwik/lib/qwik/group-config.rb, line 20
def group_config_check_exist
  @group_config.check_exist
end
inactive?(now = Time.now) click to toggle source
# File vendor/qwik/lib/qwik/group-sweep.rb, line 21
def inactive?(now = Time.now)
  return false if forward? || @group_config.permanent?
  return @db.last_article_time + @group_config[:ml_life_time] < now
end
need_alert?(now = Time.now) click to toggle source
# File vendor/qwik/lib/qwik/group-sweep.rb, line 16
def need_alert?(now = Time.now)
  return false if forward? || @group_config.permanent? || alerted?
  return @db.last_article_time + @group_config[:ml_alert_time] <= now
end
newly_created?() click to toggle source
# File vendor/qwik/lib/qwik/group-cond.rb, line 16
def newly_created?
  return ! @db.exist?(:Members)
end
prepare_confirmation(mail) click to toggle source

ml-processor.rb:115: ml.prepare_confirmation(@mail)

# File vendor/qwik/lib/qwik/group-confirm.rb, line 18
def prepare_confirmation (mail)
  # Create empty ML files.
  @members.save

  waiting_message_put(mail.bare)
  str = waiting_message_get         # FIXME: Why?

  add_waiting_member(mail.from)
  mail.collect_cc.each {|address| 
    add_waiting_member(address)
  }
  send_confirmation(mail.from)
end
remove_member(address) click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 41
def remove_member (address)
  return if ! @members.active_include?(address)
  @members.remove_member(address)
  @logger.log "[#{@name}]: Remove: #{address}"
  close if @members.active_empty?
end
report_ml_close_soon() click to toggle source
# File vendor/qwik/lib/qwik/group-sendmail.rb, line 18
def report_ml_close_soon
  return if @members.active_empty?

  name = @name

  subject = Mail.encode_field(_("[%s] ML will be closed soon", name))

  header = [
    ['To', @address],
    ['From',       @address],
    ['Subject',    subject],
    ['Reply-To',   @address],
    ['Content-Type', content_type]
  ]
  header.concat(quickml_fields)

  time_to_close = @db.last_article_time + @group_config[:ml_life_time]
  ndays = ((time_to_close - Time.now) / 86400.0).ceil
  datefmt = __('%Y-%m-%d %H:%M')

  body =  _("ML will be closed if no article is posted for %d days.\n\n",
            ndays)
  body << _("Time to close: %s.\n\n", time_to_close.strftime(datefmt))
  body << generate_footer(true)

  mail = {
    :mail_from => '', 
    :recipient => get_active_members,
    :header => header,
    :body => body,
  }
  Sendmail(@config.smtp_host, @config.smtp_port, @logger, mail)
  @logger.log "[#{@name}]: Alert: ML will be closed soon"
  close_alertedp_file
end
setup_test_config() click to toggle source
# File vendor/qwik/lib/qwik/test-module-ml.rb, line 22
def setup_test_config
  # setup config
  raise 'config not defined' if !defined?($test_config)
  @qwikconfig = $test_config

  # setup memory
  raise 'memory not defined' if !defined?($test_memory)
  @qwikmemory = $test_memory
end
submit(mail) click to toggle source
# File vendor/qwik/lib/qwik/group-sendmail.rb, line 54
def submit (mail)
  #p 'ml.submit ' if $ml_debug

  return if @members.active_empty?

  if @group_config[:max_mail_length] < mail.body.length
    report_too_large_mail(mail)
    @logger.log "[#{@name}]: Too Large Mail: #{mail.from}"
    return
  end

  reset_error_member(mail.from)
  start_time = Time.now
  _submit(mail)
  elapsed = Time.now - start_time
  msg = "[#{@name}:#{@count}]: Send:"
  msg += " #{@config.smtp_host} #{elapsed} sec." if ! $test
  @logger.log msg
end
validate_confirmation(time) click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 32
def validate_confirmation(time)
  return waiting_message_exist? && waiting_message_mtime.to_i == time.to_i
end

Private Instance Methods

_org_submit(mail) click to toggle source
# File vendor/qwik/lib/qwik/group-sendmail.rb, line 81
def _org_submit (mail)
  inc_count
  save_charset(@message_charset)
  remove_alertedp_file

  subject = Mail.rewrite_subject(mail['Subject'], @name, @count)

  body = rewrite_body(mail)

  header = []
  mail.each_field {|key, value|
    k = key.downcase
    next if k == 'subject' or k == 'reply-to'
    header.push([key, value])
  }
  header.push(['Subject',   subject],
              ['Reply-To',        @address],
              ['X-Mail-Count',@count])
  header.concat(quickml_fields)

  mail = {
    :mail_from => @return_address, 
    :recipient => get_active_members,
    :header => header,
    :body => body
  }
  Sendmail(@config.smtp_host, @config.smtp_port, @logger, mail)
end
_submit(mail) click to toggle source
# File vendor/qwik/lib/qwik/group-sendmail.rb, line 76
def _submit(mail)
  site_post(mail)
  _org_submit(mail)
end
add_waiting_member(address) click to toggle source

waiting_member

# File vendor/qwik/lib/qwik/group-confirm.rb, line 85
def add_waiting_member (address)
  waiting_members_add(address+"\n")
end
alerted?() click to toggle source
Alertedp
# File vendor/qwik/lib/qwik/group-cond.rb, line 58
def alerted?
  return @db.exist?(:Alerted)
end
close_alertedp_file() click to toggle source
# File vendor/qwik/lib/qwik/group-cond.rb, line 62
def close_alertedp_file
  @db.put(:Alerted, '')
end
confirmation_address() click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 62
def confirmation_address
  t = waiting_message_mtime.to_i
  return "confirm+#{t}+#{@address}"
end
confirmation_message(address) click to toggle source
# File vendor/qwik/lib/qwik/group-sendmail.rb, line 195
def confirmation_message(address)
  body = ''
  body += _("First, please read the agreement of this service.\n")

 #body += _("http://example.com/qwikjpAgreementE.html\n")
  body += _("http://qwik.jp/qwikjpAgreementE.html\n")
 #body += "http://#{@cnfig.domain}/"+_('AgreementE.html')+"\n"

  body += _("You must agree with this agreement to use the service.\n")
  body += _("If you agree, then,\n")
  body += _("Please simply reply to this mail to create ML <%s>.\n", address)
  body
end
content_type() click to toggle source
# File vendor/qwik/lib/qwik/group-sendmail.rb, line 181
def content_type
  return Mail.content_type(@config.content_type, @message_charset)
end
generate_header(address, added_members) click to toggle source
# File vendor/qwik/lib/qwik/group-mail.rb, line 49
def generate_header(address, added_members)
  header = "ML: #{address}\n"
  header << generate_new_member_list(added_members)
  header << "\n"
  return header
end
generate_member_list(address, list) click to toggle source
# File vendor/qwik/lib/qwik/group-mail.rb, line 96
def generate_member_list(address, list)
  return _("Members of <%s>:\n", address) + list + "\n"
end
generate_new_member_list(added_members) click to toggle source
# File vendor/qwik/lib/qwik/group-mail.rb, line 56
def generate_new_member_list(added_members)
  return added_members.map {|address|
    _("New Member: %s\n", MailAddress.obfuscate(address))
  }.join
end
generate_total_file_size_exceeded_info() click to toggle source
# File vendor/qwik/lib/qwik/group-mail.rb, line 100
def generate_total_file_size_exceeded_info
    return "\n" + 
    _('WARNING: Total attached file size exceeded.')  + " " +
    _('Files are not attached on the web.') + "\n"
end
generate_total_file_size_reaching_info() click to toggle source
# File vendor/qwik/lib/qwik/group-mail.rb, line 106
def generate_total_file_size_reaching_info
    return "\n" +
    _("WARNING: Total attached file size is reaching to the limit.") +
    " "  +
    _("%s left", (@config[:max_total_file_size] - @site.files_total).byte_format) +
    "\n"
end
generate_unsubscribe_info(address) click to toggle source
# File vendor/qwik/lib/qwik/group-mail.rb, line 87
def generate_unsubscribe_info(address)
  return "\n" +
    _("How to unsubscribe from the ML:\n") +
    _("- Just send an empty message to <%s>.\n", address) +
    _("- Alternatively, if you cannot send an empty message for some reason,\n") +
    _("  please send a message just saying 'unsubscribe' to <%s>.\n", address) +
    _("  (e.g., hotmail's advertisement, signature, etc.)\n") #'
end
get_active_members() click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 82
def get_active_members
  return @members.get_active
end
inc_count() click to toggle source
# File vendor/qwik/lib/qwik/group-cond.rb, line 33
def inc_count
  @count += 1
  @db.put(:Count, @count)
end
init_charset() click to toggle source
Charset
# File vendor/qwik/lib/qwik/group-cond.rb, line 39
def init_charset
  @charset = Group.load_charset(@db)
end
init_count() click to toggle source
Count
# File vendor/qwik/lib/qwik/group-cond.rb, line 23
def init_count
  @count = Group.load_count(@db)
end
init_group_config() click to toggle source
# File vendor/qwik/lib/qwik/group-config.rb, line 30
def init_group_config
  @group_config = GroupConfig.new(@db)
  group_config = GroupConfig.get_group_config(@config)
  @group_config.set_default(group_config)
  @group_config.write
end
init_members() click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 74
def init_members
  @members = GroupMembers.new(@config, self, @db, @group_config)
end
init_site() click to toggle source
# File vendor/qwik/lib/qwik/group-site.rb, line 20
def init_site
  @groupsite = GroupSite.new(@config, @name)
  @site = @groupsite.site
end
member_added?() click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 78
def member_added?
  return ! @added_members.empty?
end
page_url() click to toggle source
# File vendor/qwik/lib/qwik/group-site.rb, line 34
def page_url
  key = @groupsite.key
  base = ''
  base = "#{key}.html" if key
 #return "http://#{@config.ml_domain}/#{@name}/#{base}"
  return "#{@config.public_url}#{@name}/#{base}"
end
quickml_fields() click to toggle source
# File vendor/qwik/lib/qwik/group-sendmail.rb, line 185
def quickml_fields
  return [
    ['Precedence',   'bulk'],
    ['X-ML-Address', @address],
    ['X-ML-Name',   @name],
    ['X-ML-Info',   @config.public_url],
    ['X-QuickML',   'true']
  ]
end
remove_alertedp_file() click to toggle source
# File vendor/qwik/lib/qwik/group-cond.rb, line 66
def remove_alertedp_file
  @db.delete(:Alerted)
end
report_removed_member(error_address) click to toggle source
# File vendor/qwik/lib/qwik/group-sendmail.rb, line 154
def report_removed_member (error_address)
  return if @members.active_empty?
  subject = Mail.encode_field(_("[%s] Removed: <%s>", 
                                @name, error_address))
  header = [
    ['To', @address],
    ['From',       @address],
    ['Subject',    subject],
    ['Reply-To',   @address],
    ['Content-Type', content_type]
  ]
  header.concat(quickml_fields)
  body =  _("<%s> was removed from the mailing list:\n<%s>\n", 
            error_address, @address)
  body << _("because the address was unreachable.\n")
  body << generate_footer(true)

  mail = {
    :mail_from => '', 
    :recipient => get_active_members,
    :header => header,
    :body => body,
  }
  Sendmail(@config.smtp_host, @config.smtp_port, @logger, mail)
  @logger.log "[#{@name}]: Notify: Remove #{error_address}"
end
report_too_large_mail(mail) click to toggle source

FIXME: too similar to report_too_large_mail in ml-session.rb

# File vendor/qwik/lib/qwik/group-sendmail.rb, line 129
def report_too_large_mail (mail)
  header = []
  subject = Mail.encode_field(_("[QuickML] Error: %s", mail['Subject']))
  header.push(['To',        mail.from],
              ['From',    @address],
              ['Subject', subject],
              ['Content-Type', content_type])
  max  = @group_config[:max_mail_length].commify
  body =   _("Sorry, your mail exceeds the length limitation.\n")
  body <<  _("The max length is %s bytes.\n\n", max)
  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"

  mail = {
    :mail_from => '', 
    :recipient => mail.from,
    :header => header,
    :body => body,
  }
  Sendmail(@config.smtp_host, @config.smtp_port, @logger, mail)
end
reset_error_member(address) click to toggle source
# File vendor/qwik/lib/qwik/group-member.rb, line 67
def reset_error_member (address)
  return unless @members.error_include?(address)
  @members.error_delete(address)
  @logger.log "[#{@name}]: ResetError: #{address}"
  @members.save
end
rewrite_body(mail) click to toggle source
# File vendor/qwik/lib/qwik/group-mail.rb, line 18
def rewrite_body (mail)
  # body header
  header = generate_header(@address, @added_members) if member_added?

  # body footer
  footer = generate_footer

  if mail.multipart?
    parts = mail.parts
    sub_mail = Mail.new
    sub_mail.read(parts.first)
    cte = sub_mail['Content-Transfer-Encoding']
    if sub_mail.content_type == 'text/plain' and cte.nil? or cte.empty? or cte =~ /^[78]bit$/
      sub_mail.body = header + sub_mail.body if header
      sub_mail.body += footer
    end
    parts[0] = sub_mail.to_s
    mail.body = Mail.join_parts(parts, mail.boundary)
    return mail.body
  end

  cte = mail['Content-Transfer-Encoding']
  if mail.plain_text_body? and cte.nil? or cte.empty? or cte =~ /^[78]bit$/
    mail.body = header + mail.body if header
    mail.body += footer
    return mail.body
  end

  return mail.body  # abandon
end
save_charset(charset) click to toggle source
# File vendor/qwik/lib/qwik/group-cond.rb, line 52
def save_charset(charset)
  return if charset.nil?
  @db.put(:Charset, charset+"\n")
end
send_confirmation(creator_address) click to toggle source
# File vendor/qwik/lib/qwik/group-sendmail.rb, line 110
def send_confirmation (creator_address)
  header = []
  subject = Mail.encode_field(_("[%s] Confirmation: %s", @name, @address))
  header.push(['To',        creator_address],
              ['From',    confirmation_address],
              ['Subject', subject],
              ['Content-Type', content_type])
  body = confirmation_message(@address)
  mail = {
    :mail_from => '', 
    :recipient => creator_address,
    :header => header,
    :body => body,
  }
  Sendmail(@config.smtp_host, @config.smtp_port, @logger, mail)
  @logger.log "[#{@name}]: Send confirmation: #{confirmation_address} #{creator_address}"
end
site_post(mail, test=false) click to toggle source
# File vendor/qwik/lib/qwik/group-site.rb, line 25
def site_post(mail, test=false)
  begin     # Do not raise exception when posting a message.
    @groupsite.post(mail)
    @logger.log("[#{@name}]: QwikPost: #{@groupsite.key}")
  rescue
    @logger.log("[#{@name}]: QwikPostError: "+$!.to_s+$!.backtrace.to_s)
  end
end
total_file_size_exceeded?() click to toggle source
# File vendor/qwik/lib/qwik/group-site.rb, line 55
def total_file_size_exceeded?
  @groupsite.total_file_size_exceeded
end
total_file_size_reaching?() click to toggle source
# File vendor/qwik/lib/qwik/group-site.rb, line 59
def total_file_size_reaching?
  @groupsite.total_file_size_reaching
end
waiting_members_add(content) click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 94
def waiting_members_add(content)
  @db.add(:WaitingMembers, content)
end
waiting_members_delete() click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 97
def waiting_members_delete
  @db.delete(:WaitingMembers)
end
waiting_members_exist?() click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 88
def waiting_members_exist?
  return @db.exist?(:WaitingMembers)
end
waiting_members_get() click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 91
def waiting_members_get
  return @db.get(:WaitingMembers)
end
waiting_message_delete() click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 80
def waiting_message_delete
  @db.delete(:WaitingMessage)
end
waiting_message_exist?() click to toggle source

waiting_message

# File vendor/qwik/lib/qwik/group-confirm.rb, line 68
def waiting_message_exist?
  return @db.exist?(:WaitingMessage)
end
waiting_message_get() click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 77
def waiting_message_get
  return @db.get(:WaitingMessage)
end
waiting_message_mtime() click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 71
def waiting_message_mtime
  return @db.mtime(:WaitingMessage)
end
waiting_message_put(content) click to toggle source
# File vendor/qwik/lib/qwik/group-confirm.rb, line 74
def waiting_message_put(content)
  @db.put(:WaitingMessage, content)
end