class Mail::Parsers::AddressListsParser

Public Instance Methods

parse(s) click to toggle source
# File lib/mail/parsers/address_lists_parser.rb, line 5
def parse(s)
  address_list = AddressListStruct.new([],[])

  if Mail::Utilities.blank?(s)
    return address_list
  end

  actions, error = Ragel.parse(:address_lists, s)
  if error
    raise Mail::Field::ParseError.new(Mail::AddressList, s, error)
  end


  phrase_s = phrase_e = qstr_s = qstr = comment_s = nil
  group_name_s = domain_s = group_name = nil
  local_dot_atom_s = local_dot_atom_e = nil
  local_dot_atom_pre_comment_e = nil
  obs_domain_list_s = nil

  address_s = 0
  address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
  actions.each_slice(2) do |action_id, p|
    action = Mail::Parsers::Ragel::ACTIONS[action_id]
    case action

    # Phrase
    when :phrase_s then phrase_s = p
    when :phrase_e then phrase_e = p-1

    # Quoted String.
    when :qstr_s then qstr_s = p
    when :qstr_e then qstr = s[qstr_s..(p-1)]

    # Comment
    when :comment_s
      comment_s = p unless comment_s
    when :comment_e
      if address
        address.comments << s[comment_s..(p-2)]
      end
      comment_s = nil

    # Group Name
    when :group_name_s then group_name_s = p
    when :group_name_e
      if qstr
        group = qstr
        qstr = nil
      else
        group = s[group_name_s..(p-1)]
        group_name_s = nil
      end
      address_list.group_names << group
      group_name = group

      # Start next address
      address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
      address_s = p
      address.group = group_name

    # Address
    when :address_s
      address_s = p
    when :address_e
      # Ignore address end events if they don't have
      # a matching address start event.
      next if address_s.nil?
      if address.local.nil? && local_dot_atom_pre_comment_e && local_dot_atom_s && local_dot_atom_e
        if address.domain
          address.local = s[local_dot_atom_s..local_dot_atom_e] if address
        else
          address.local = s[local_dot_atom_s..local_dot_atom_pre_comment_e] if address
        end
      end
      address.raw = s[address_s..(p-1)]
      address_list.addresses << address if address

      # Start next address
      address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
      address.group = group_name
      address_s = nil

    # Don't set the display name until the
    # address has actually started. This allows
    # us to choose quoted_s version if it
    # exists and always use the 'full' phrase
    # version.
    when :angle_addr_s
      if qstr
        address.display_name = qstr
        qstr = nil
      elsif phrase_e
        address.display_name = s[phrase_s..phrase_e].strip
        phrase_e = phrase_s = nil
      end

    # Domain
    when :domain_s
      domain_s = p
    when :domain_e
      address.domain = s[domain_s..(p-1)].rstrip if address

    # Local
    when :local_dot_atom_s then local_dot_atom_s = p
    when :local_dot_atom_e then local_dot_atom_e = p-1
    when :local_dot_atom_pre_comment_e
      local_dot_atom_pre_comment_e = p-1
    when :local_quoted_string_e
      address.local = '"' + qstr + '"' if address

    # obs_domain_list
    when :obs_domain_list_s then obs_domain_list_s = p
    when :obs_domain_list_e
      address.obs_domain_list = s[obs_domain_list_s..(p-1)]

    else
      raise Mail::Field::ParseError.new(Mail::AddressList, s, "Failed to process unknown action: #{action}")
    end
  end

  if address_list.addresses.empty? && address_list.group_names.empty?
    raise Mail::Field::ParseError.new(Mail::AddressList, s, "Didn't find any addresses or groups")
  end

  address_list
end