email_forward_parser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 108a3d5da682072760ed99217ad68f469c4a600db7452ec60386a6e1f504c791
4
+ data.tar.gz: ab7efb48ceeeef0f8a27377b56b2a86f0008c00c794005db55de03f326ea9b4e
5
+ SHA512:
6
+ metadata.gz: 32fb415bacbbecc88b045d3b6eb517db25a166670129808186e4ab57b1167ee1b654383ba7c6027426f4b33f5ce8f73bdd101672fd718cbe5f1e20274b3c1ee0
7
+ data.tar.gz: 85bff86e606bdede3d3905c121c3ceb1fbe76d9ffd369be4df71a2263149a1c7b7ec5fa5819fe2f305dc7d1cc14159de93b954ee5b2f5d6810d056ab76983a76
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/testdouble/standard
3
+ ruby_version: 3.2
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in email_forward_parser.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
11
+
12
+ gem "standard", "~> 1.3"
data/Gemfile.lock ADDED
@@ -0,0 +1,62 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ email_forward_parser (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ json (2.6.3)
11
+ language_server-protocol (3.17.0.3)
12
+ lint_roller (1.0.0)
13
+ minitest (5.18.1)
14
+ parallel (1.23.0)
15
+ parser (3.2.2.3)
16
+ ast (~> 2.4.1)
17
+ racc
18
+ racc (1.7.1)
19
+ rainbow (3.1.1)
20
+ rake (13.0.6)
21
+ regexp_parser (2.8.1)
22
+ rexml (3.2.5)
23
+ rubocop (1.52.1)
24
+ json (~> 2.3)
25
+ parallel (~> 1.10)
26
+ parser (>= 3.2.2.3)
27
+ rainbow (>= 2.2.2, < 4.0)
28
+ regexp_parser (>= 1.8, < 3.0)
29
+ rexml (>= 3.2.5, < 4.0)
30
+ rubocop-ast (>= 1.28.0, < 2.0)
31
+ ruby-progressbar (~> 1.7)
32
+ unicode-display_width (>= 2.4.0, < 3.0)
33
+ rubocop-ast (1.29.0)
34
+ parser (>= 3.2.1.0)
35
+ rubocop-performance (1.18.0)
36
+ rubocop (>= 1.7.0, < 2.0)
37
+ rubocop-ast (>= 0.4.0)
38
+ ruby-progressbar (1.13.0)
39
+ standard (1.29.0)
40
+ language_server-protocol (~> 3.17.0.2)
41
+ lint_roller (~> 1.0)
42
+ rubocop (~> 1.52.0)
43
+ standard-custom (~> 1.0.0)
44
+ standard-performance (~> 1.1.0)
45
+ standard-custom (1.0.1)
46
+ lint_roller (~> 1.0)
47
+ standard-performance (1.1.0)
48
+ lint_roller (~> 1.0)
49
+ rubocop-performance (~> 1.18.0)
50
+ unicode-display_width (2.4.2)
51
+
52
+ PLATFORMS
53
+ arm64-darwin-22
54
+
55
+ DEPENDENCIES
56
+ email_forward_parser!
57
+ minitest (~> 5.0)
58
+ rake (~> 13.0)
59
+ standard (~> 1.3)
60
+
61
+ BUNDLED WITH
62
+ 2.4.13
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # EmailForwardParser
2
+
3
+ A Ruby port of the [crisp-oss/email-forward-parser](https://github.com/crisp-oss/email-forward-parser) Javascript library.
4
+
5
+ Not intended for public usage. The port is feature complete for our use but it is not idiomatic Ruby.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ require "standard/rake"
13
+
14
+ task default: %i[test standard]
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/email_forward_parser/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "email_forward_parser"
7
+ spec.version = EmailForwardParser::VERSION
8
+ spec.authors = ["Sean Collins"]
9
+ spec.email = ["dev@buildr.com"]
10
+
11
+ spec.summary = "Parse forwarded emails from body and subject"
12
+ spec.homepage = "https://github.com/buildrtech/email_forward_parser"
13
+ spec.license = "Nonstandard"
14
+ spec.required_ruby_version = ">= 3.2.2"
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(__dir__) do
19
+ `git ls-files -z`.split("\x0").reject do |f|
20
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .github .tool-versions])
21
+ end
22
+ end
23
+
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ # Uncomment to register a new dependency of your gem
29
+ # spec.add_dependency "example-gem", "~> 1.0"
30
+
31
+ # For more information and examples about making a new gem, check out our
32
+ # guide at: https://bundler.io/guides/creating_gem.html
33
+ end
@@ -0,0 +1,82 @@
1
+ module EmailForwardParser
2
+ class MailboxParser
3
+ def initialize(regexes:, text:)
4
+ @regexes = regexes
5
+ @text = text
6
+ end
7
+
8
+ def call
9
+ match = Utils.match_regexes(regexes, text)
10
+
11
+ if match && match.length > 0
12
+ mailboxes_line = match[match.length - 1].strip
13
+
14
+ if mailboxes_line
15
+ mailboxes = []
16
+
17
+ until mailboxes_line.empty?
18
+ mailbox_match = Utils.match_regexes(Regexes::MAILBOX, mailboxes_line)
19
+
20
+ # Address and/or name available?
21
+ if mailbox_match && mailbox_match.length > 0
22
+ address = nil
23
+ name = nil
24
+
25
+ # Address and name available?
26
+ if mailbox_match.length == 3
27
+ address = mailbox_match[2].strip
28
+ name = mailbox_match[1].strip
29
+ else
30
+ address = mailbox_match[1]&.strip
31
+ end
32
+
33
+ cleaned_name = if name&.empty?
34
+ nil
35
+ else
36
+ name
37
+ end
38
+
39
+ mailboxes.push({
40
+ address:,
41
+
42
+ # Some clients fill the name with the address \
43
+ # ("bessie.berry@acme.com <bessie.berry@acme.com>")
44
+ name: (address != cleaned_name) ? cleaned_name : nil
45
+ })
46
+
47
+ # Remove matched mailbox from mailboxes line
48
+ mailboxes_line = mailboxes_line.sub(mailbox_match[0], "").strip
49
+
50
+ if mailboxes_line
51
+ # Remove leading mailboxes separator \
52
+ # (", Nicholas <nicholas@globex.corp>")
53
+ Regexes::MAILBOXES_SEPARATORS.each do |separator|
54
+ if mailboxes_line[0] == separator
55
+ mailboxes_line = mailboxes_line[1..].strip
56
+ break
57
+ end
58
+ end
59
+ end
60
+ else
61
+ mailboxes.push({
62
+ address: mailboxes_line.strip,
63
+ name: nil
64
+ })
65
+
66
+ # No more matches
67
+ mailboxes_line = ""
68
+ end
69
+ end
70
+
71
+ return mailboxes
72
+ end
73
+ end
74
+
75
+ []
76
+ end
77
+
78
+ private
79
+
80
+ attr_reader :regexes, :text
81
+ end
82
+ end
@@ -0,0 +1,223 @@
1
+ require_relative "./regexes"
2
+ require_relative "./utils"
3
+ require_relative "./mailbox_parser"
4
+
5
+ module EmailForwardParser
6
+ class OriginalEmailParser
7
+ def initialize(text, body)
8
+ # Remove Byte Order Mark
9
+ text.gsub!(Regexes::BYTE_ORDER_MARK, "")
10
+
11
+ # Remove ">" at the beginning of each line, while keeping line breaks
12
+ text.gsub!(Regexes::QUOTE_LINE_BREAK, "")
13
+
14
+ # Remove ">" at the beginning of other lines
15
+ text.gsub!(Regexes::QUOTE, "")
16
+
17
+ # Remove " " at the beginning of lines
18
+ text.gsub!(Regexes::FOUR_SPACES, "")
19
+
20
+ @text = text
21
+ @body = body
22
+ end
23
+
24
+ def call
25
+ {
26
+ body: parse_original_body,
27
+ from: parse_original_from,
28
+ to: parse_original_to,
29
+ cc: parse_original_cc,
30
+ subject: parse_original_subject,
31
+ date: parse_original_date
32
+ }
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :text, :body
38
+
39
+ def parse_original_body
40
+ match = nil
41
+
42
+ # First method: extract the text after the Cc, To or Reply-To part \
43
+ # (Apple Mail, Gmail) or after the Subject part (Outlook Live)
44
+ regexes = [
45
+ Regexes::ORIGINAL_SUBJECT,
46
+ Regexes::ORIGINAL_CC,
47
+ Regexes::ORIGINAL_TO,
48
+ Regexes::ORIGINAL_REPLY_TO
49
+ ]
50
+
51
+ regexes.each do |regex|
52
+ match = Utils.split_regexes(regex, text)
53
+
54
+ # A new line must be present between the Cc, To, Reply-To or Subject \
55
+ # part and the actual body
56
+ return match[2].strip if match && match.length == 3 && match[2].start_with?("\n\n")
57
+ end
58
+
59
+ # Second method: extract the text after the Subject part \
60
+ # (New Outlook 2019, Yahoo Mail)
61
+ match = Utils.split_regexes(
62
+ Regexes::ORIGINAL_SUBJECT + Regexes::ORIGINAL_SUBJECT_LAX,
63
+ text
64
+ )
65
+
66
+ # No new line must be present between the Subject part and the actual body
67
+ return match[2].strip if match && match.length == 3
68
+
69
+ # Third method: return the raw text, as there is no original information \
70
+ # embedded (no Cc, To, Subject, etc.) (Outlook 2019)
71
+ text
72
+ end
73
+
74
+ def parse_mailbox(regexes, text)
75
+ MailboxParser.new(regexes:, text:).call
76
+ end
77
+
78
+ def parse_original_from
79
+ address = nil
80
+ name = nil
81
+
82
+ # First method: extract the author via the From part (Apple Mail, Gmail,
83
+ # Outlook Live, New Outlook 2019, Thunderbird)
84
+ author = parse_mailbox(Regexes::ORIGINAL_FROM, text)
85
+
86
+ # Author found?
87
+ return author.first if author.any? && author.first[:address] && !author.first[:address].empty?
88
+
89
+ # Second method: extract the author via the separator (Outlook 2019)
90
+ match = Utils.match_regexes(Regexes::SEPARATOR, body)
91
+
92
+ if match && match.length == 4 && match.named_captures
93
+ # Notice: the order of parts may change depending on the localization,
94
+ # hence the use of named groups
95
+ address = match.named_captures["from_address"].strip
96
+ name = match.named_captures["from_name"].strip
97
+
98
+ return {
99
+ address:,
100
+
101
+ # Some clients fill the name with the address
102
+ # ("bessie.berry@acme.com <bessie.berry@acme.com>")
103
+ name: (address != name) ? name : nil
104
+ }
105
+ end
106
+
107
+ # Third method: extract the author via the From part, using lax regexes
108
+ # (Yahoo Mail)
109
+ match = Utils.match_regexes(Regexes::ORIGINAL_FROM_LAX, text)
110
+
111
+ if match && match.length > 1
112
+ address = match[3].strip
113
+ name = match[2].strip
114
+
115
+ return {
116
+ address:,
117
+
118
+ # Some clients fill the name with the address
119
+ # ("bessie.berry@acme.com <bessie.berry@acme.com>")
120
+ name: (address != name) ? name : nil
121
+ }
122
+ end
123
+
124
+ {
125
+ address:,
126
+ name:
127
+ }
128
+ end
129
+
130
+ def parse_original_to
131
+ # First method: extract the primary recipient(s) via the To part
132
+ # (Apple Mail, Gmail, Outlook Live, New Outlook 2019, Thunderbird)
133
+ recipients = parse_mailbox(
134
+ Regexes::ORIGINAL_TO,
135
+ text
136
+ )
137
+
138
+ # Recipient(s) found?
139
+ return recipients if recipients.any?
140
+
141
+ # Second method: the Subject, Date, and Cc parts are stuck to the To part,
142
+ # remove them before attempting a new extract, using lax regexes (Yahoo Mail)
143
+ clean_text = Utils.replace_regexes(Regexes::ORIGINAL_SUBJECT_LAX, text)
144
+
145
+ clean_text = Utils.replace_regexes(Regexes::ORIGINAL_DATE_LAX, clean_text)
146
+
147
+ clean_text = Utils.replace_regexes(Regexes::ORIGINAL_CC_LAX, clean_text)
148
+
149
+ parse_mailbox(
150
+ Regexes::ORIGINAL_TO_LAX,
151
+ clean_text
152
+ )
153
+ end
154
+
155
+ def parse_original_cc
156
+ # First method: extract the carbon-copy recipient(s) via the Cc part
157
+ # (Apple Mail, Gmail, Outlook Live, New Outlook 2019, Thunderbird)
158
+ recipients = parse_mailbox(
159
+ Regexes::ORIGINAL_CC,
160
+ text
161
+ )
162
+
163
+ # Recipient(s) found?
164
+ return recipients if recipients.any?
165
+
166
+ # Second method: the Subject and Date parts are stuck to the To part,
167
+ # remove them before attempting a new extract, using lax regexes (Yahoo Mail)
168
+ clean_text = Utils.replace_regexes(Regexes::ORIGINAL_SUBJECT_LAX, text)
169
+
170
+ clean_text = Utils.replace_regexes(Regexes::ORIGINAL_DATE_LAX, clean_text)
171
+
172
+ parse_mailbox(
173
+ Regexes::ORIGINAL_CC_LAX,
174
+ clean_text
175
+ )
176
+ end
177
+
178
+ def parse_original_subject
179
+ # First method: extract the subject via the Subject part
180
+ # (Apple Mail, Gmail, Outlook Live, New Outlook 2019, Thunderbird)
181
+ match = Utils.match_regexes(Regexes::ORIGINAL_SUBJECT, text)
182
+
183
+ return match[1].strip if match && match.length > 0
184
+
185
+ # Second method: extract the subject via the Subject part, using lax regexes (Yahoo Mail)
186
+ match = Utils.match_regexes(Regexes::ORIGINAL_SUBJECT_LAX, text)
187
+
188
+ return unless match && match.length > 0
189
+
190
+ match[1].strip
191
+ end
192
+
193
+ def parse_original_date
194
+ # First method: extract the date via the Date part
195
+ # (Apple Mail, Gmail, Outlook Live, New Outlook 2019, Thunderbird)
196
+ match = Utils.match_regexes(Regexes::ORIGINAL_DATE, text)
197
+
198
+ return match[1].strip if match && match.length > 0
199
+
200
+ # Second method: extract the date via the separator (Outlook 2019)
201
+ match = Utils.match_regexes(Regexes::SEPARATOR, body)
202
+
203
+ if match && match.length == 4 && match.named_captures.any?
204
+ # Notice: the order of parts may change depending on the localization,
205
+ # hence the use of named groups
206
+ return match.named_captures["date"].strip
207
+ end
208
+
209
+ # Third method: the Subject part is stuck to the Date part, remove it
210
+ # before attempting a new extract, using lax regexes (Yahoo Mail)
211
+ clean_text = Utils.replace_regexes(
212
+ Regexes::ORIGINAL_SUBJECT_LAX,
213
+ text
214
+ )
215
+
216
+ match = Utils.match_regexes(Regexes::ORIGINAL_DATE_LAX, clean_text)
217
+
218
+ return unless match && match.length > 0
219
+
220
+ match[1].strip
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,57 @@
1
+ require_relative "./regexes"
2
+ require_relative "./utils"
3
+
4
+ module EmailForwardParser
5
+ class Parser
6
+ def parse_subject(subject)
7
+ match = Utils.match_regexes(Regexes::SUBJECT, subject)
8
+
9
+ # Notice: return an empty string if the detected subject is empty
10
+ # (e.g. 'Fwd: ')
11
+ match[1].strip || "" if match && match.length > 1
12
+ end
13
+
14
+ def parse_body(body, forwarded:)
15
+ # Replace carriage return by regular line break
16
+ body.gsub!(/#{Regexes::CARRIAGE_RETURN}/o, "\n")
17
+
18
+ # Remove Byte Order Mark
19
+ body.gsub!(/#{Regexes::BYTE_ORDER_MARK}/o, "")
20
+
21
+ # First method: split via the separator (Apple Mail, Gmail, Outlook Live,
22
+ # Outlook 2019, Yahoo Mail, Thunderbird)
23
+ match = Utils.split_regexes(Regexes::SEPARATOR, body)
24
+
25
+ if match && match.length > 1
26
+ return {
27
+ body:,
28
+ message: match[0].strip,
29
+ email: match[-1].strip
30
+ }
31
+ end
32
+
33
+ # Attempt second method?
34
+ # Notice: as this second method is more uncertain (we split via the From
35
+ # part, without further verification), we have to be sure we can
36
+ # attempt it. The `forwarded` boolean gives the confirmation that the
37
+ # email was indeed forwarded (detected from the Subject part)
38
+ if forwarded
39
+ # Second method: split via the Form part (New Outlook 2019)
40
+ match = Utils.split_regexes(Regexes::ORIGINAL_FROM, body)
41
+
42
+ if match && match.length > 3
43
+ # The From has been detached by `split`, attach it back
44
+ email = match[1] + match[match.length - 1]
45
+
46
+ return {
47
+ body:,
48
+ message: match[0].strip,
49
+ email: email.strip
50
+ }
51
+ end
52
+ end
53
+
54
+ {}
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,331 @@
1
+ module EmailForwardParser
2
+ module Regexes
3
+ QUOTE_LINE_BREAK = /^(>+)\s?$/m # Apple Mail
4
+ QUOTE = /^(>+)\s?/m # Apple Mail
5
+ FOUR_SPACES = /^(\ {4})\s?/m # Outlook 2019
6
+ CARRIAGE_RETURN = /\r\n/m # Outlook 2019
7
+ BYTE_ORDER_MARK = /\uFEFF/m # Outlook 2019
8
+ SEPARATOR = [
9
+ /^>?\s*Begin forwarded message\s?:/m, # Apple Mail (en)
10
+ /^>?\s*Začátek přeposílané zprávy\s?:/m, # Apple Mail (cs)
11
+ /^>?\s*Start på videresendt besked\s?:/m, # Apple Mail (da)
12
+ /^>?\s*Anfang der weitergeleiteten Nachricht\s?:/m, # Apple Mail (de)
13
+ /^>?\s*Inicio del mensaje reenviado\s?:/m, # Apple Mail (es)
14
+ /^>?\s*Välitetty viesti alkaa\s?:/m, # Apple Mail (fi)
15
+ /^>?\s*Début du message réexpédié\s?:/m, # Apple Mail (fr)
16
+ /^>?\s*Début du message transféré\s?:/m, # Apple Mail iOS (fr)
17
+ /^>?\s*Započni proslijeđenu poruku\s?:/m, # Apple Mail (hr)
18
+ /^>?\s*Továbbított levél kezdete\s?:/m, # Apple Mail (hu)
19
+ /^>?\s*Inizio messaggio inoltrato\s?:/m, # Apple Mail (it)
20
+ /^>?\s*Begin doorgestuurd bericht\s?:/m, # Apple Mail (nl)
21
+ /^>?\s*Videresendt melding\s?:/m, # Apple Mail (no)
22
+ /^>?\s*Początek przekazywanej wiadomości\s?:/m, # Apple Mail (pl)
23
+ /^>?\s*Início da mensagem reencaminhada\s?:/m, # Apple Mail (pt)
24
+ /^>?\s*Início da mensagem encaminhada\s?:/m, # Apple Mail (pt-br)
25
+ /^>?\s*Începe mesajul redirecționat\s?:/m, # Apple Mail (ro)
26
+ /^>?\s*Начало переадресованного сообщения\s?:/m, # Apple Mail (ro)
27
+ /^>?\s*Začiatok preposlanej správy\s?:/m, # Apple Mail (sk)
28
+ /^>?\s*Vidarebefordrat mejl\s?:/m, # Apple Mail (sv)
29
+ /^>?\s*İleti başlangıcı\s?:/m, # Apple Mail (tr)
30
+ /^>?\s*Початок листа, що пересилається\s?:/m, # Apple Mail (uk)
31
+ /^\s*-{8,10}\s*Forwarded message\s*-{8,10}\s*/m, # Gmail (all locales)
32
+ /^\s*_{32}\s*$/m, # Outlook Live (all locales)
33
+ /^\s?Dne\s?(?<date>.+),\s?(?<from_name>.+)\s*[\[|<](?<from_address>.+)[\]|>]\s?napsal\(a\)\s?:/m, # Outlook 2019 (cz)
34
+ /^\s?D.\s?(?<date>.+)\s?skrev\s?"(?<from_name>.+)"\s*[\[|<](?<from_address>.+)[\]|>]\s?:/m, # Outlook 2019 (da)
35
+ /^\s?Am\s?(?<date>.+)\s?schrieb\s?"(?<from_name>.+)"\s*[\[|<](?<from_address>.+)[\]|>]\s?:/m, # Outlook 2019 (de)
36
+ /^\s?On\s?(?<date>.+),\s?"(?<from_name>.+)"\s*[\[|<](?<from_address>.+)[\]|>]\s?wrote\s?:/m, # Outlook 2019 (en)
37
+ /^\s?El\s?(?<date>.+),\s?"(?<from_name>.+)"\s*[\[|<](?<from_address>.+)[\]|>]\s?escribió\s?:/m, # Outlook 2019 (es)
38
+ /^\s?Le\s?(?<date>.+),\s?«(?<from_name>.+)»\s*[\[|<](?<from_address>.+)[\]|>]\s?a écrit\s?:/m, # Outlook 2019 (fr)
39
+ /^\s?(?<from_name>.+)\s*[\[|<](?<from_address>.+)[\]|>]\s?kirjoitti\s?(?<date>.+)\s?:/m, # Outlook 2019 (fi)
40
+ /^\s?(?<date>.+)\s?időpontban\s?(?<from_name>.+)\s*[\[|<(](?<from_address>.+)[\]|>)]\s?ezt írta\s?:/m, # Outlook 2019 (hu)
41
+ /^\s?Il giorno\s?(?<date>.+)\s?"(?<from_name>.+)"\s*[\[|<](?<from_address>.+)[\]|>]\s?ha scritto\s?:/m, # Outlook 2019 (it)
42
+ /^\s?Op\s?(?<date>.+)\s?heeft\s?(?<from_name>.+)\s*[\[|<](?<from_address>.+)[\]|>]\s?geschreven\s?:/m, # Outlook 2019 (nl)
43
+ /^\s?(?<from_name>.+)\s*[\[|<](?<from_address>.+)[\]|>]\s?skrev følgende den\s?(?<date>.+)\s?:/m, # Outlook 2019 (no)
44
+ /^\s?Dnia\s?(?<date>.+)\s?„(?<from_name>.+)”\s*[\[|<](?<from_address>.+)[\]|>]\s?napisał\s?:/m, # Outlook 2019 (pl)
45
+ /^\s?Em\s?(?<date>.+),\s?"(?<from_name>.+)"\s*[\[|<](?<from_address>.+)[\]|>]\s?escreveu\s?:/m, # Outlook 2019 (pt)
46
+ /^\s?(?<date>.+)\s?пользователь\s?"(?<from_name>.+)"\s*[\[|<](?<from_address>.+)[\]|>]\s?написал\s?:/m, # Outlook 2019 (ru)
47
+ /^\s?(?<date>.+)\s?používateľ\s?(?<from_name>.+)\s*\([\[|<](?<from_address>.+)[\]|>]\)\s?napísal\s?:/m, # Outlook 2019 (sk)
48
+ /^\s?Den\s?(?<date>.+)\s?skrev\s?"(?<from_name>.+)"\s*[\[|<](?<from_address>.+)[\]|>]\s?följande\s?:/m, # Outlook 2019 (sv)
49
+ /^\s?"(?<from_name>.+)"\s*[\[|<](?<from_address>.+)[\]|>],\s?(?<date>.+)\s?tarihinde şunu yazdı\s?:/m, # Outlook 2019 (tr)
50
+ /\s*-{5,8} Přeposlaná zpráva -{5,8}\s*/m, # Yahoo Mail (cs), Thunderbird (cs)
51
+ /\s*-{5,8} Videresendt meddelelse -{5,8}\s*/m, # Yahoo Mail (da), Thunderbird (da)
52
+ /\s*-{5,8} Weitergeleitete Nachricht -{5,8}\s*/m, # Yahoo Mail (de), Thunderbird (de)
53
+ /\s*-{5,8} Forwarded Message -{5,8}\s*/m, # Yahoo Mail (en), Thunderbird (en)
54
+ /\s*-{5,8} Mensaje reenviado -{5,8}\s*/m, # Yahoo Mail (es), Thunderbird (es)
55
+ /\s*-{5} Edelleenlähetetty viesti -{5}\s*/m, # Yahoo Mail (fi)
56
+ /\s*-{5} Message transmis -{5}\s*/m, # Yahoo Mail (fr)
57
+ /\s*-{5,8} Továbbított üzenet -{5,8}\s*/m, # Yahoo Mail (hu), Thunderbird (hu)
58
+ /\s*-{5} Messaggio inoltrato -{5}\s*/m, # Yahoo Mail (it)
59
+ /\s*-{5,8} Doorgestuurd bericht -{5,8}\s*/m, # Yahoo Mail (nl), Thunderbird (nl)
60
+ /\s*-{5,8} Videresendt melding -{5,8}\s*/m, # Yahoo Mail (no), Thunderbird (no)
61
+ /\s*-{5} Przekazana wiadomość -{5}\s*/m, # Yahoo Mail (pl)
62
+ /\s*-{5,8} Mensagem reencaminhada -{5,8}\s*/m, # Yahoo Mail (pt), Thunderbird (pt)
63
+ /\s*-{5,8} Mensagem encaminhada -{5,8}\s*/m, # Yahoo Mail (pt-br), Thunderbird (pt-br)
64
+ /\s*-{5,8} Mesaj redirecționat -{5,8}\s*/m, # Yahoo Mail (ro)
65
+ /\s*-{5} Пересылаемое сообщение -{5}\s*/m, # Yahoo Mail (ru)
66
+ /\s*-{5} Preposlaná správa -{5}\s*/m, # Yahoo Mail (sk)
67
+ /\s*-{5,8} Vidarebefordrat meddelande -{5,8}\s*/m, # Yahoo Mail (sv), Thunderbird (sv)
68
+ /\s*-{5} İletilmiş Mesaj -{5}\s*/m, # Yahoo Mail (tr)
69
+ /\s*-{5} Перенаправлене повідомлення -{5}\s*/m, # Yahoo Mail (uk)
70
+ %r{\s*-{8} Välitetty viesti / Fwd.Msg -{8}\s*}m, # Thunderbird (fi)
71
+ /\s*-{8} Message transféré -{8}\s*/m, # Thunderbird (fr)
72
+ /\s*-{8} Proslijeđena poruka -{8}\s*/m, # Thunderbird (hr)
73
+ /\s*-{8} Messaggio Inoltrato -{8}\s*/m, # Thunderbird (it)
74
+ /\s*-{3} Treść przekazanej wiadomości -{3}\s*/m, # Thunderbird (pl)
75
+ /\s*-{8} Перенаправленное сообщение -{8}\s*/m, # Thunderbird (ru)
76
+ /\s*-{8} Preposlaná správa --- Forwarded Message -{8}\s*/m, # Thunderbird (sk)
77
+ /\s*-{8} İletilen İleti -{8}\s*/m, # Thunderbird (tr)
78
+ /\s*-{8} Переслане повідомлення -{8}\s*/m # Thunderbird (uk)
79
+ ].freeze
80
+
81
+ SUBJECT = [
82
+ /^Fw:(.*)/, # Outlook Live (cs, en, hr, hu, sk), Yahoo Mail (all locales)
83
+ /^VS:(.*)/, # Outlook Live (da), New Outlook 2019 (da)
84
+ /^WG:(.*)/, # Outlook Live (de), New Outlook 2019 (de)
85
+ /^RV:(.*)/, # Outlook Live (es), New Outlook 2019 (es)
86
+ /^TR:(.*)/, # Outlook Live (fr), New Outlook 2019 (fr)
87
+ /^I:(.*)/, # Outlook Live (it), New Outlook 2019 (it)
88
+ /^FW:(.*)/, # Outlook Live (nl, pt), New Outlook 2019 (cs, en, hu, nl, pt, ru, sk), Outlook 2019 (all locales)
89
+ /^Vs:(.*)/, # Outlook Live (no)
90
+ /^PD:(.*)/, # Outlook Live (pl), New Outlook 2019 (pl)
91
+ /^ENC:(.*)/, # Outlook Live (pt-br), New Outlook 2019 (pt-br)
92
+ /^Redir.:(.*)/, # Outlook Live (ro)
93
+ /^VB:(.*)/, # Outlook Live (sv), New Outlook 2019 (sv)
94
+ /^VL:(.*)/, # New Outlook 2019 (fi)
95
+ /^Videresend:(.*)/, # New Outlook 2019 (no)
96
+ /^İLT:(.*)/, # New Outlook 2019 (tr)
97
+ /^Fwd:(.*)/ # Gmail (all locales), Thunderbird (all locales)
98
+ ].freeze
99
+
100
+ ORIGINAL_FROM = [
101
+ /^(\s*From\s?:(.+))$/, # Apple Mail (en), Outlook Live (all locales), New Outlook 2019 (en), Thunderbird (da, en)
102
+ /^(\s*Od\s?:(.+))$/, # Apple Mail (cs, pl, sk), Gmail (cs, pl, sk), New Outlook 2019 (cs, pl, sk), Thunderbird (cs, sk)
103
+ /^(\s*Fra\s?:(.+))$/, # Apple Mail (da, no), Gmail (da, no), New Outlook 2019 (da), Thunderbird (no)
104
+ /^(\s*Von\s?:(.+))$/, # Apple Mail (de), Gmail (de), New Outlook 2019 (de), Thunderbird (de)
105
+ /^(\s*De\s?:(.+))$/, # Apple Mail (es, fr, pt, pt-br), Gmail (es, fr, pt, pt-br), New Outlook 2019 (es, fr, pt, pt-br), Thunderbird (fr, pt, pt-br)
106
+ /^(\s*Lähettäjä\s?:(.+))$/, # Apple Mail (fi), Gmail (fi), New Outlook 2019 (fi), Thunderbird (fi)
107
+ /^(\s*Šalje\s?:(.+))$/, # Apple Mail (hr), Gmail (hr), Thunderbird (hr)
108
+ /^(\s*Feladó\s?:(.+))$/, # Apple Mail (hu), Gmail (hu), New Outlook 2019 (fr), Thunderbird (hu)
109
+ /^(\s*Da\s?:(.+))$/, # Apple Mail (it), Gmail (it), New Outlook 2019 (it)
110
+ /^(\s*Van\s?:(.+))$/, # Apple Mail (nl), Gmail (nl), New Outlook 2019 (nl), Thunderbird (nl)
111
+ /^(\s*Expeditorul\s?:(.+))$/, # Apple Mail (ro)
112
+ /^(\s*Отправитель\s?:(.+))$/, # Apple Mail (ru)
113
+ /^(\s*Från\s?:(.+))$/, # Apple Mail (sv), Gmail (sv), New Outlook 2019 (sv), Thunderbird (sv)
114
+ /^(\s*Kimden\s?:(.+))$/, # Apple Mail (tr), Thunderbird (tr)
115
+ /^(\s*Від кого\s?:(.+))$/, # Apple Mail (uk)
116
+ /^(\s*Saatja\s?:(.+))$/, # Gmail (et)
117
+ /^(\s*De la\s?:(.+))$/, # Gmail (ro)
118
+ /^(\s*Gönderen\s?:(.+))$/, # Gmail (tr)
119
+ /^(\s*От\s?:(.+))$/, # Gmail (ru), New Outlook 2019 (ru), Thunderbird (ru)
120
+ /^(\s*Від\s?:(.+))$/, # Gmail (uk), Thunderbird (uk)
121
+ /^(\s*Mittente\s?:(.+))$/, # Thunderbird (it)
122
+ /^(\s*Nadawca\s?:(.+))$/, # Thunderbird (pl)
123
+ /^(\s*de la\s?:(.+))$/ # Thunderbird (ro)
124
+ ].freeze
125
+
126
+ ORIGINAL_FROM_LAX = [
127
+ /(\s*From\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (en)
128
+ /(\s*Od\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (cs, pl, sk)
129
+ /(\s*Fra\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (da, no)
130
+ /(\s*Von\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (de)
131
+ /(\s*De\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (es, fr, pt, pt-br)
132
+ /(\s*Lähettäjä\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (fi)
133
+ /(\s*Feladó\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (hu)
134
+ /(\s*Da\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (it)
135
+ /(\s*Van\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (nl)
136
+ /(\s*De la\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (ro)
137
+ /(\s*От\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (ru)
138
+ /(\s*Från\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (sv)
139
+ /(\s*Kimden\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/, # Yahoo Mail (tr)
140
+ /(\s*Від\s?:(.+?)\s?\n?\s*[\[|<](.+?)[\]|>])/ # Yahoo Mail (uk)
141
+ ].freeze
142
+
143
+ ORIGINAL_SUBJECT = [
144
+ /^Subject\s?:(.+)/i, # Apple Mail (en), Gmail (all locales), Outlook Live (all locales), New Outlook 2019 (en), Thunderbird (da, en)
145
+ /^Předmět\s?:(.+)/i, # Apple Mail (cs), New Outlook 2019 (cs), Thunderbird (cs)
146
+ /^Emne\s?:(.+)/i, # Apple Mail (da, no), New Outlook 2019 (da), Thunderbird (no)
147
+ /^Betreff\s?:(.+)/i, # Apple Mail (de), New Outlook 2019 (de), Thunderbird (de)
148
+ /^Asunto\s?:(.+)/i, # Apple Mail (es), New Outlook 2019 (es), Thunderbird (es)
149
+ /^Aihe\s?:(.+)/i, # Apple Mail (fi), New Outlook 2019 (fi), Thunderbird (fi)
150
+ /^Objet\s?:(.+)/i, # Apple Mail (fr), New Outlook 2019 (fr)
151
+ /^Predmet\s?:(.+)/i, # Apple Mail (hr, sk), New Outlook 2019 (sk), Thunderbird (sk)
152
+ /^Tárgy\s?:(.+)/i, # Apple Mail (hu), New Outlook 2019 (hu), Thunderbird (hu)
153
+ /^Oggetto\s?:(.+)/i, # Apple Mail (it), New Outlook 2019 (it), Thunderbird (it)
154
+ /^Onderwerp\s?:(.+)/i, # Apple Mail (nl), New Outlook 2019 (nl), Thunderbird (nl)
155
+ /^Temat\s?:(.+)/i, # Apple Mail (pl), New Outlook 2019 (pl), Thunderbird (pl)
156
+ /^Assunto\s?:(.+)/i, # Apple Mail (pt, pt-br), New Outlook 2019 (pt, pt-br), Thunderbird (pt, pt-br)
157
+ /^Subiectul\s?:(.+)/i, # Apple Mail (ro), Thunderbird (ro)
158
+ /^Тема\s?:(.+)/i, # Apple Mail (ru, uk), New Outlook 2019 (ru), Thunderbird (ru, uk)
159
+ /^Ämne\s?:(.+)/i, # Apple Mail (sv), New Outlook 2019 (sv), Thunderbird (sv)
160
+ /^Konu\s?:(.+)/i, # Apple Mail (tr), Thunderbird (tr)
161
+ /^Sujet\s?:(.+)/i, # Thunderbird (fr)
162
+ /^Naslov\s?:(.+)/i # Thunderbird (hr)
163
+ ].freeze
164
+
165
+ ORIGINAL_SUBJECT_LAX = [
166
+ /Subject\s?:(.+)/i, # Yahoo Mail (en)
167
+ /Emne\s?:(.+)/i, # Yahoo Mail (da, no)
168
+ /Předmět\s?:(.+)/i, # Yahoo Mail (cs)
169
+ /Betreff\s?:(.+)/i, # Yahoo Mail (de)
170
+ /Asunto\s?:(.+)/i, # Yahoo Mail (es)
171
+ /Aihe\s?:(.+)/i, # Yahoo Mail (fi)
172
+ /Objet\s?:(.+)/i, # Yahoo Mail (fr)
173
+ /Tárgy\s?:(.+)/i, # Yahoo Mail (hu)
174
+ /Oggetto\s?:(.+)/i, # Yahoo Mail (it)
175
+ /Onderwerp\s?:(.+)/i, # Yahoo Mail (nl)
176
+ /Assunto\s?:?(.+)/i, # Yahoo Mail (pt, pt-br)
177
+ /Temat\s?:(.+)/i, # Yahoo Mail (pl)
178
+ /Subiect\s?:(.+)/i, # Yahoo Mail (ro)
179
+ /Тема\s?:(.+)/i, # Yahoo Mail (ru, uk)
180
+ /Predmet\s?:(.+)/i, # Yahoo Mail (sk)
181
+ /Ämne\s?:(.+)/i, # Yahoo Mail (sv)
182
+ /Konu\s?:(.+)/i # Yahoo Mail (tr)
183
+ ].freeze
184
+
185
+ ORIGINAL_TO = [
186
+ /^\s*To\s?:(.+)$/, # Apple Mail (en), Gmail (all locales), Outlook Live (all locales), Thunderbird (da, en)
187
+ /^\s*Komu\s?:(.+)$/, # Apple Mail (cs), New Outlook 2019 (cs, sk), Thunderbird (cs)
188
+ /^\s*Til\s?:(.+)$/, # Apple Mail (da, no), New Outlook 2019 (da), Thunderbird (no)
189
+ /^\s*An\s?:(.+)$/, # Apple Mail (de), New Outlook 2019 (de), Thunderbird (de)
190
+ /^\s*Para\s?:(.+)$/, # Apple Mail (es, pt, pt-br), New Outlook 2019 (es, pt, pt-br), Thunderbird (es, pt, pt-br)
191
+ /^\s*Vastaanottaja\s?:(.+)$/, # Apple Mail (fi), New Outlook 2019 (fi), Thunderbird (fi)
192
+ /^\s*À\s?:(.+)$/, # Apple Mail (fr), New Outlook 2019 (fr)
193
+ /^\s*Prima\s?:(.+)$/, # Apple Mail (hr), Thunderbird (hr)
194
+ /^\s*Címzett\s?:(.+)$/, # Apple Mail (hu), New Outlook 2019 (hu), Thunderbird (hu)
195
+ /^\s*A\s?:(.+)$/, # Apple Mail (it), New Outlook 2019 (it), Thunderbird (it)
196
+ /^\s*Aan\s?:(.+)$/, # Apple Mail (nl), New Outlook 2019 (nl), Thunderbird (nl)
197
+ /^\s*Do\s?:(.+)$/, # Apple Mail (pl), New Outlook 2019 (pl)
198
+ /^\s*Destinatarul\s?:(.+)$/, # Apple Mail (ro)
199
+ /^\s*Кому\s?:(.+)$/, # Apple Mail (ru, uk), New Outlook 2019 (ru), Thunderbird (ru, uk)
200
+ /^\s*Pre\s?:(.+)$/, # Apple Mail (sk), Thunderbird (sk)
201
+ /^\s*Till\s?:(.+)$/, # Apple Mail (sv), New Outlook 2019 (sv), Thunderbird (sv)
202
+ /^\s*Kime\s?:(.+)$/, # Apple Mail (tr), Thunderbird (tr)
203
+ /^\s*Pour\s?:(.+)$/, # Thunderbird (fr)
204
+ /^\s*Adresat\s?:(.+)$/ # Thunderbird (pl)
205
+ ].freeze
206
+
207
+ ORIGINAL_TO_LAX = [
208
+ /\s*To\s?:(.+)$/, # Yahoo Mail (en)
209
+ /\s*Komu\s?:(.+)$/, # Yahoo Mail (cs, sk)
210
+ /\s*Til\s?:(.+)$/, # Yahoo Mail (da, no, sv)
211
+ /\s*An\s?:(.+)$/, # Yahoo Mail (de)
212
+ /\s*Para\s?:(.+)$/, # Yahoo Mail (es, pt, pt-br)
213
+ /\s*Vastaanottaja\s?:(.+)$/, # Yahoo Mail (fi)
214
+ /\s*À\s?:(.+)$/, # Yahoo Mail (fr)
215
+ /\s*Címzett\s?:(.+)$/, # Yahoo Mail (hu)
216
+ /\s*A\s?:(.+)$/, # Yahoo Mail (it)
217
+ /\s*Aan\s?:(.+)$/, # Yahoo Mail (nl)
218
+ /\s*Do\s?:(.+)$/, # Yahoo Mail (pl)
219
+ /\s*Către\s?:(.+)$/, # Yahoo Mail (ro), Thunderbird (ro)
220
+ /\s*Кому\s?:(.+)$/, # Yahoo Mail (ru, uk)
221
+ /\s*Till\s?:(.+)$/, # Yahoo Mail (sv)
222
+ /\s*Kime\s?:(.+)$/ # Yahoo Mail (tr)
223
+ ]
224
+
225
+ ORIGINAL_CC = [
226
+ /^\s*Cc\s?:(.+)$/, # Apple Mail (en, da, es, fr, hr, it, pt, pt-br, ro, sk), Gmail (all locales), Outlook Live (all locales), New Outlook 2019 (da, de, en, fr, it, pt-br)
227
+ /^\s*CC\s?:(.+)$/, # New Outlook 2019 (es, nl, pt), Thunderbird (da, en, es, fi, hr, hu, it, nl, no, pt, pt-br, ro, tr, uk)
228
+ /^\s*Kopie\s?:(.+)$/, # Apple Mail (cs, de, nl), New Outlook 2019 (cs), Thunderbird (cs)
229
+ /^\s*Kopio\s?:(.+)$/, # Apple Mail (fi), New Outlook 2019 (es)
230
+ /^\s*Másolat\s?:(.+)$/, # Apple Mail (hu)
231
+ /^\s*Kopi\s?:(.+)$/, # Apple Mail (no)
232
+ /^\s*Dw\s?:(.+)$/, # Apple Mail (pl)
233
+ /^\s*Копия\s?:(.+)$/, # Apple Mail (ru), New Outlook 2019 (ru), Thunderbird (ru)
234
+ /^\s*Kopia\s?:(.+)$/, # Apple Mail (sv), New Outlook 2019 (sv), Thunderbird (pl, sv)
235
+ /^\s*Bilgi\s?:(.+)$/, # Apple Mail (tr)
236
+ /^\s*Копія\s?:(.+)$/, # Apple Mail (uk),
237
+ /^\s*Másolatot kap\s?:(.+)$/, # New Outlook 2019 (hu)
238
+ /^\s*Kópia\s?:(.+)$/, # New Outlook 2019 (sk), Thunderbird (sk)
239
+ /^\s*DW\s?:(.+)$/, # New Outlook 2019 (pl)
240
+ /^\s*Kopie \(CC\)\s?:(.+)$/, # Thunderbird (de)
241
+ /^\s*Copie à\s?:(.+)$/ # Thunderbird (fr)
242
+ ].freeze
243
+
244
+ ORIGINAL_CC_LAX = [
245
+ /\s*Cc\s?:(.+)$/, # Yahoo Mail (da, en, it, nl, pt, pt-br, ro, tr)
246
+ /\s*CC\s?:(.+)$/, # Yahoo Mail (de, es)
247
+ /\s*Kopie\s?:(.+)$/, # Yahoo Mail (cs)
248
+ /\s*Kopio\s?:(.+)$/, # Yahoo Mail (fi)
249
+ /\s*Másolat\s?:(.+)$/, # Yahoo Mail (hu)
250
+ /\s*Kopi\s?:(.+)$/, # Yahoo Mail (no)
251
+ /\s*Dw\s?(.+)$/, # Yahoo Mail (pl)
252
+ /\s*Копия\s?:(.+)$/, # Yahoo Mail (ru)
253
+ /\s*Kópia\s?:(.+)$/, # Yahoo Mail (sk)
254
+ /\s*Kopia\s?:(.+)$/, # Yahoo Mail (sv)
255
+ /\s*Копія\s?:(.+)$/ # Yahoo Mail (uk)
256
+ ].freeze
257
+
258
+ ORIGINAL_REPLY_TO = [
259
+ /^\s*Reply-To\s?:(.+)$/, # Apple Mail (en)
260
+ /^\s*Odgovori na\s?:(.+)$/, # Apple Mail (hr)
261
+ /^\s*Odpověď na\s?:(.+)$/, # Apple Mail (cs)
262
+ /^\s*Svar til\s?:(.+)$/, # Apple Mail (da)
263
+ /^\s*Antwoord aan\s?:(.+)$/, # Apple Mail (nl)
264
+ /^\s*Vastaus\s?:(.+)$/, # Apple Mail (fi)
265
+ /^\s*Répondre à\s?:(.+)$/, # Apple Mail (fr)
266
+ /^\s*Antwort an\s?:(.+)$/, # Apple Mail (de)
267
+ /^\s*Válaszcím\s?:(.+)$/, # Apple Mail (hu)
268
+ /^\s*Rispondi a\s?:(.+)$/, # Apple Mail (it)
269
+ /^\s*Svar til\s?:(.+)$/, # Apple Mail (no)
270
+ /^\s*Odpowiedź-do\s?:(.+)$/, # Apple Mail (pl)
271
+ /^\s*Responder A\s?:(.+)$/, # Apple Mail (pt)
272
+ /^\s*Responder a\s?:(.+)$/, # Apple Mail (pt-br, es)
273
+ /^\s*Răspuns către\s?:(.+)$/, # Apple Mail (ro)
274
+ /^\s*Ответ-Кому\s?:(.+)$/, # Apple Mail (ru)
275
+ /^\s*Odpovedať-Pre\s?:(.+)$/, # Apple Mail (sk)
276
+ /^\s*Svara till\s?:(.+)$/, # Apple Mail (sv)
277
+ /^\s*Yanıt Adresi\s?:(.+)$/, # Apple Mail (tr)
278
+ /^\s*Кому відповісти\s?:(.+)$/ # Apple Mail (uk)
279
+ ].freeze
280
+
281
+ ORIGINAL_DATE = [
282
+ /^\s*Date\s?:(.+)$/, # Apple Mail (en, fr), Gmail (all locales), New Outlook 2019 (en, fr), Thunderbird (da, en, fr)
283
+ /^\s*Datum\s?:(.+)$/, # Apple Mail (cs, de, hr, nl, sv), New Outlook 2019 (cs, de, nl, sv), Thunderbird (cs, de, hr, nl, sv)
284
+ /^\s*Dato\s?:(.+)$/, # Apple Mail (da, no), New Outlook 2019 (da), Thunderbird (no)
285
+ /^\s*Fecha\s?:(.+)$/, # Apple Mail (es), New Outlook 2019 (es), Thunderbird (es)
286
+ /^\s*Päivämäärä\s?:(.+)$/, # Apple Mail (fi), New Outlook 2019 (fi)
287
+ /^\s*Dátum\s?:(.+)$/, # Apple Mail (hu, sk), New Outlook 2019 (sk), Thunderbird (hu, sk)
288
+ /^\s*Data\s?:(.+)$/, # Apple Mail (it, pl, pt, pt-br), New Outlook 2019 (it, pl, pt, pt-br), Thunderbird (it, pl, pt, pt-br)
289
+ /^\s*Dată\s?:(.+)$/, # Apple Mail (ro), Thunderbird (ro)
290
+ /^\s*Дата\s?:(.+)$/, # Apple Mail (ru, uk), New Outlook 2019 (ru), Thunderbird (ru, uk)
291
+ /^\s*Tarih\s?:(.+)$/, # Apple Mail (tr), Thunderbird (tr)
292
+ /^\s*Sent\s?:(.+)$/, # Outlook Live (all locales)
293
+ /^\s*Päiväys\s?:(.+)$/ # Thunderbird (fi)
294
+ ].freeze
295
+
296
+ ORIGINAL_DATE_LAX = [
297
+ /\s*Datum\s?:(.+)$/m, # Yahoo Mail (cs)
298
+ /\s*Sendt\s?:(.+)$/m, # Yahoo Mail (da, no)
299
+ /\s*Gesendet\s?:(.+)$/m, # Yahoo Mail (de)
300
+ /\s*Sent\s?:(.+)$/m, # Yahoo Mail (en)
301
+ /\s*Enviado\s?:(.+)$/m, # Yahoo Mail (es, pt, pt-br)
302
+ /\s*Envoyé\s?:(.+)$/m, # Yahoo Mail (fr)
303
+ /\s*Lähetetty\s?:(.+)$/m, # Yahoo Mail (fi)
304
+ /\s*Elküldve\s?:(.+)$/m, # Yahoo Mail (hu)
305
+ /\s*Inviato\s?:(.+)$/m, # Yahoo Mail (it)
306
+ /\s*Verzonden\s?:(.+)$/m, # Yahoo Mail (it)
307
+ /\s*Wysłano\s?:(.+)$/m, # Yahoo Mail (pl)
308
+ /\s*Trimis\s?:(.+)$/m, # Yahoo Mail (ro)
309
+ /\s*Отправлено\s?:(.+)$/m, # Yahoo Mail (ru)
310
+ /\s*Odoslané\s?:(.+)$/m, # Yahoo Mail (sk)
311
+ /\s*Skickat\s?:(.+)$/m, # Yahoo Mail (sv)
312
+ /\s*Gönderilen\s?:(.+)$/m, # Yahoo Mail (tr)
313
+ /\s*Відправлено\s?:(.+)$/m # Yahoo Mail (uk)
314
+ ].freeze
315
+
316
+ MAILBOXES_SEPARATORS = [
317
+ ",", # Apple Mail, Gmail, New Outlook 2019, Thunderbird
318
+ ";" # Outlook Live, Yahoo Mail
319
+ ].freeze
320
+
321
+ MAILBOX = [
322
+ /^(.+?)\s?\n?\s*[\[|<]mailto:(.+?)[\]|>]/, # "Walter Sheltan <mailto:walter.sheltan@acme.com>" or "Walter Sheltan [mailto:walter.sheltan@acme.com]" or "walter.sheltan@acme.com <mailto:walter.sheltan@acme.com>"
323
+ /^'(.+?)'\s?\n?\s*[\[|<](.+?)[\]|>]/, # 'Walter Sheltan' <walter.sheltan@acme.com>" or "'Walter Sheltan' [walter.sheltan@acme.com]" or "'walter.sheltan@acme.com' <walter.sheltan@acme.com>"
324
+ /^"'(.+?)'"\s?\n?\s*[\[|<](.+?)[\]|>]/, # ""'Walter Sheltan'" <walter.sheltan@acme.com>" or ""'Walter Sheltan'" [walter.sheltan@acme.com]" or ""'walter.sheltan@acme.com'" <walter.sheltan@acme.com>"
325
+ /^"(.+?)"\s?\n?\s*[\[|<](.+?)[\]|>]/, # ""Walter Sheltan" <walter.sheltan@acme.com>" or ""Walter Sheltan" [walter.sheltan@acme.com]" or ""walter.sheltan@acme.com" <walter.sheltan@acme.com>"
326
+ /^([^,;]+?)\s?\n?\s*[\[|<](.+?)[\]|>]/, # "Walter Sheltan <walter.sheltan@acme.com>" or "Walter Sheltan [walter.sheltan@acme.com]" or "walter.sheltan@acme.com <walter.sheltan@acme.com>"
327
+ /^(.?)\s?\n?\s*[\[|<](.+?)[\]|>]/, # "<walter.sheltan@acme.com>"
328
+ /^([^\s@]+@[^\s@]+\.[^\s@,]+)/ # "walter.sheltan@acme.com"
329
+ ].freeze
330
+ end
331
+ end
@@ -0,0 +1,33 @@
1
+ module EmailForwardParser
2
+ module Utils
3
+ def self.match_regexes(regexes, str)
4
+ regexes.find do |regex|
5
+ match = str.match(regex)
6
+
7
+ return match unless match.to_a.empty?
8
+ end
9
+ end
10
+
11
+ def self.split_regexes(regexes, str)
12
+ regexes.find do |regex|
13
+ match = str.split(regex)
14
+
15
+ return match if match.length > 1
16
+ end
17
+ end
18
+
19
+ def self.replace_regexes(regexes, str)
20
+ match = nil
21
+
22
+ regexes.each do |regex|
23
+ match = str.gsub(regex, "")
24
+
25
+ # A successful replace must return a smaller string than the input
26
+
27
+ break if match.length < str.length
28
+ end
29
+
30
+ match
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EmailForwardParser
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "email_forward_parser/version"
4
+ require_relative "email_forward_parser/parser"
5
+ require_relative "email_forward_parser/original_email_parser"
6
+
7
+ module EmailForwardParser
8
+ class Error < StandardError; end
9
+
10
+ def self.read(body:, subject: nil)
11
+ parser = Parser.new
12
+ email = {}
13
+ result = {}
14
+
15
+ # Check if email was forwarded or not (via the subject)
16
+ parsed_subject = parser.parse_subject(subject) if subject
17
+
18
+ forwarded = parsed_subject || false
19
+
20
+ # Check if email was forwarded or not (via the body)
21
+ if !subject || forwarded
22
+ result = parser.parse_body(body, forwarded:)
23
+
24
+ if result[:email]
25
+ forwarded = true
26
+
27
+ email = OriginalEmailParser.new(result[:email], result[:body]).call
28
+ end
29
+ end
30
+
31
+ message = if result[:message] && result[:message].empty?
32
+ nil
33
+ else
34
+ result[:message]
35
+ end
36
+ {
37
+ forwarded:,
38
+
39
+ message:,
40
+
41
+ email: {
42
+ body: email[:body],
43
+
44
+ from: {
45
+ address: email.dig(:from, :address),
46
+ name: email.dig(:from, :name)
47
+ },
48
+
49
+ to: email[:to],
50
+
51
+ cc: email[:cc],
52
+
53
+ subject: parsed_subject || email[:subject],
54
+ date: email[:date]
55
+ }
56
+ }
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: email_forward_parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Collins
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-06-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - dev@buildr.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".standard.yml"
21
+ - Gemfile
22
+ - Gemfile.lock
23
+ - README.md
24
+ - Rakefile
25
+ - email_forward_parser.gemspec
26
+ - lib/email_forward_parser.rb
27
+ - lib/email_forward_parser/mailbox_parser.rb
28
+ - lib/email_forward_parser/original_email_parser.rb
29
+ - lib/email_forward_parser/parser.rb
30
+ - lib/email_forward_parser/regexes.rb
31
+ - lib/email_forward_parser/utils.rb
32
+ - lib/email_forward_parser/version.rb
33
+ homepage: https://github.com/buildrtech/email_forward_parser
34
+ licenses:
35
+ - Nonstandard
36
+ metadata: {}
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 3.2.2
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubygems_version: 3.4.12
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Parse forwarded emails from body and subject
56
+ test_files: []