email_forward_parser 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []