File: address_lists_parser.rl

package info (click to toggle)
ruby-mail 2.8.1%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,704 kB
  • sloc: ruby: 73,709; makefile: 3
file content (179 lines) | stat: -rw-r--r-- 4,738 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# frozen_string_literal: true
require 'mail/utilities'
require 'mail/parser_tools'

begin
  original_verbose, $VERBOSE = $VERBOSE, nil

%%{
  machine address_lists;
  alphtype int;

  # Phrase
  action phrase_s { phrase_s = p }
  action phrase_e { phrase_e = p-1 }

  # Quoted String.
  action qstr_s { qstr_s = p }
  action qstr_e { qstr = chars(data, qstr_s, p-1) }

  # Comment
  action comment_s { comment_s = p unless comment_s }
  action comment_e {
    if address
      address.comments << chars(data, comment_s, p-2)
    end
    comment_s = nil
  }

  # Group Name
  action group_name_s { group_name_s = p }
  action group_name_e {
    if qstr
      group = qstr
      qstr = nil
    else
      group = chars(data, 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
  action address_s { address_s = p }

  # Ignore address end events without a start event.
  action address_e {
    if address_s
      if address.local.nil? && local_dot_atom_pre_comment_e && local_dot_atom_s && local_dot_atom_e
        if address.domain
          address.local = chars(data, local_dot_atom_s, local_dot_atom_e)
        else
          address.local = chars(data, local_dot_atom_s, local_dot_atom_pre_comment_e)
        end
      end
      address.raw = chars(data, 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
    end
  }

  # 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.
  action angle_addr_s {
    if qstr
      address.display_name = Mail::Utilities.unescape(qstr)
      qstr = nil
    elsif phrase_e
      address.display_name = chars(data, phrase_s, phrase_e).strip
      phrase_e = phrase_s = nil
    end
  }

  # Domain
  action domain_s { domain_s = p }
  action domain_e {
    address.domain = chars(data, domain_s, p-1).rstrip if address
  }

  # Local
  action local_dot_atom_s { local_dot_atom_s = p }
  action local_dot_atom_e { local_dot_atom_e = p-1 }
  action local_dot_atom_pre_comment_e { local_dot_atom_pre_comment_e = p-1 }
  action local_quoted_string_e { address.local = '"' + qstr + '"' if address }

  # obs_domain_list
  action obs_domain_list_s { obs_domain_list_s = p }
  action obs_domain_list_e { address.obs_domain_list = chars(data, obs_domain_list_s, p-1) }

  # Junk actions
  action addr_spec { }
  action ctime_date_e { }
  action ctime_date_s { }
  action date_e { }
  action date_s { }
  action disp_type_e { }
  action disp_type_s { }
  action encoding_e { }
  action encoding_s { }
  action main_type_e { }
  action main_type_s { }
  action major_digits_e { }
  action major_digits_s { }
  action minor_digits_e { }
  action minor_digits_s { }
  action msg_id_e { }
  action msg_id_s { }
  action param_attr_e { }
  action param_attr_s { }
  action param_val_e { }
  action param_val_s { }
  action received_tokens_e { }
  action received_tokens_s { }
  action sub_type_e { }
  action sub_type_s { }
  action time_e { }
  action time_s { }
  action token_string_e { }
  action token_string_s { }

  include rfc5322 "rfc5322.rl";
  main := address_lists;
}%%

module Mail::Parsers
  module AddressListsParser
    extend Mail::ParserTools

    AddressListStruct = Struct.new(:addresses, :group_names, :error)
    AddressStruct = Struct.new(:raw, :domain, :comments, :local,
                             :obs_domain_list, :display_name, :group, :error)

    %%write data noprefix;

    def self.parse(data)
      data = data.dup.force_encoding(Encoding::ASCII_8BIT) if data.respond_to?(:force_encoding)

      address_list = AddressListStruct.new([], [])
      return address_list if Mail::Utilities.blank?(data)

      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)

      # 5.1 Variables Used by Ragel
      p = 0
      eof = pe = data.length
      stack = []

      %%write init;
      %%write exec;

      if p != eof || cs < %%{ write first_final; }%%
        raise Mail::Field::IncompleteParseError.new(Mail::AddressList, data, p)
      end

      address_list
    end
  end
end

ensure
  $VERBOSE = original_verbose
end