sisimai 5.0.1 → 5.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rake-test.yml +55 -0
- data/ChangeLog.md +27 -0
- data/README-JA.md +13 -13
- data/README.md +13 -14
- data/lib/sisimai/fact.rb +46 -8
- data/lib/sisimai/lhost/amazonses.rb +0 -1
- data/lib/sisimai/lhost/amazonworkmail.rb +0 -1
- data/lib/sisimai/lhost/aol.rb +0 -1
- data/lib/sisimai/lhost/bigfoot.rb +0 -1
- data/lib/sisimai/lhost/domino.rb +0 -1
- data/lib/sisimai/lhost/exchange2007.rb +1 -1
- data/lib/sisimai/lhost/exim.rb +7 -16
- data/lib/sisimai/lhost/facebook.rb +0 -1
- data/lib/sisimai/lhost/googlegroups.rb +2 -1
- data/lib/sisimai/lhost/gsuite.rb +0 -1
- data/lib/sisimai/lhost/mailru.rb +8 -17
- data/lib/sisimai/lhost/messagelabs.rb +0 -1
- data/lib/sisimai/lhost/mfilter.rb +1 -1
- data/lib/sisimai/lhost/mxlogic.rb +8 -18
- data/lib/sisimai/lhost/office365.rb +1 -1
- data/lib/sisimai/lhost/outlook.rb +0 -1
- data/lib/sisimai/lhost/postfix.rb +0 -1
- data/lib/sisimai/lhost/receivingses.rb +0 -1
- data/lib/sisimai/lhost/sendgrid.rb +1 -3
- data/lib/sisimai/lhost/sendmail.rb +0 -1
- data/lib/sisimai/lhost/yandex.rb +0 -1
- data/lib/sisimai/message.rb +13 -4
- data/lib/sisimai/reason/authfailure.rb +1 -0
- data/lib/sisimai/reason/blocked.rb +2 -0
- data/lib/sisimai/reason/expired.rb +1 -0
- data/lib/sisimai/reason/mailboxfull.rb +1 -0
- data/lib/sisimai/reason/securityerror.rb +1 -0
- data/lib/sisimai/reason/spamdetected.rb +1 -0
- data/lib/sisimai/reason/suspend.rb +1 -0
- data/lib/sisimai/rfc5322.rb +120 -64
- data/lib/sisimai/rhost/mimecast.rb +9 -2
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/maildir/bsd/lhost-sendmail-60.eml +85 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7090710f97dbdd17e7bf971fdad787f7eeab1656de2e93012e32430b9a7d64fb
|
4
|
+
data.tar.gz: 4dd096852de6cb0c9a44a78fc11058a86494fb4e032f8aa6c8a31bf65149cec8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4f9752e00950e8965c6328ddea6f82f80e436e9e7788d41ab9e5484fce8a64be99c23d193d902f118d158eed5246b4601f4c9ce618cd219e04a943db79df860
|
7
|
+
data.tar.gz: ee66c1d20a46c49a11fc07c111039a3322fc89cd2e682d9b09a59040f35fa3d2a93348c975f7d6cb10884d29c80962df4e6c65e51e7a90dfe03b2fd0c36cad4e
|
@@ -0,0 +1,55 @@
|
|
1
|
+
name: rake test
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches: ["5-stable"]
|
5
|
+
pull_request:
|
6
|
+
branches: ["5-stable"]
|
7
|
+
jobs:
|
8
|
+
test-cruby:
|
9
|
+
name: rake test with CRuby ${{ matrix.cruby }}
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
strategy:
|
12
|
+
fail-fast: false
|
13
|
+
matrix:
|
14
|
+
cruby: ["2.4", "3.3"]
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v4
|
17
|
+
- name: Setup CRuby
|
18
|
+
uses: ruby/setup-ruby@v1
|
19
|
+
with:
|
20
|
+
ruby-version: ${{ matrix.cruby }}
|
21
|
+
- run: gem install minitest -v 5.15.0
|
22
|
+
- run: gem install oj -v 3.10.0
|
23
|
+
- run: gem install bundle rake
|
24
|
+
- name: Cache the dependent gems
|
25
|
+
uses: actions/cache@v2
|
26
|
+
with:
|
27
|
+
path: ./cache
|
28
|
+
key: ${{ runner.os }}-ruby-${{ matrix.cruby }}-${{ hashFiles('./gem.snapshot') }}
|
29
|
+
- run: ruby -v
|
30
|
+
- run: rake publictest
|
31
|
+
- run: rake privatetest
|
32
|
+
test-jruby:
|
33
|
+
name: rake test with JRuby ${{ matrix.jruby }}
|
34
|
+
runs-on: ubuntu-latest
|
35
|
+
strategy:
|
36
|
+
fail-fast: false
|
37
|
+
matrix:
|
38
|
+
jruby: ["jruby-9.2", "jruby-9.4"]
|
39
|
+
steps:
|
40
|
+
- uses: actions/checkout@v2
|
41
|
+
- name: Setup JRuby
|
42
|
+
uses: ruby/setup-ruby@v1
|
43
|
+
with:
|
44
|
+
ruby-version: ${{ matrix.jruby }}
|
45
|
+
- run: gem install minitest -v 5.15.0
|
46
|
+
- run: gem install bundle rake jrjackson
|
47
|
+
- name: Cache the dependent gems
|
48
|
+
uses: actions/cache@v2
|
49
|
+
with:
|
50
|
+
path: ./cache
|
51
|
+
key: ${{ runner.os }}-ruby-${{ matrix.jruby }}-${{ hashFiles('./gem.snapshot') }}
|
52
|
+
- run: ruby -v
|
53
|
+
- run: rake publictest
|
54
|
+
- run: rake privatetest
|
55
|
+
|
data/ChangeLog.md
CHANGED
@@ -3,6 +3,33 @@ RELEASE NOTES for Ruby version of Sisimai
|
|
3
3
|
- releases: "https://github.com/sisimai/rb-sisimai/releases"
|
4
4
|
- download: "https://rubygems.org/gems/sisimai"
|
5
5
|
|
6
|
+
v5.0.2
|
7
|
+
---------------------------------------------------------------------------------------------------
|
8
|
+
- release: "Wed, 13 Mar 2024 13:00:00 +0900 (JST)"
|
9
|
+
- version: "5.0.2"
|
10
|
+
- changes:
|
11
|
+
- #271 #267 Sisimai 5 works on JRuby again. Thanks to @hiroyuki-sato
|
12
|
+
- #159 Implement workarounds at `Sisimai::Fact` and some public tests with `DateTime` class to
|
13
|
+
run Sisimai on JRuby, to avoid failing `strptime()`
|
14
|
+
- #269 Replace `Array#append` with `Array#push` at `Sisimai::RFC5322` because `Array#append` is
|
15
|
+
only available in Ruby 2.5 and above
|
16
|
+
- We have started testing JRuby 9.2 and 9.4 on GitHub Actions
|
17
|
+
- Disable 2 tests in `test/public/mail-test.rb` that fails on GitHub Actions only
|
18
|
+
- `5.7.23` returned from Office365 is an error related to SPF vilation (authfailure)
|
19
|
+
- #272 Fixed an issue that Sisimai could not get the value of `alias` address correctly when an
|
20
|
+
email forwarded and bounced
|
21
|
+
- `Sisimai::RFC5322.received` now returns a list including all the elements except date time and
|
22
|
+
(comments) found in the `Received` header
|
23
|
+
- Update the error message patterns in `Sisimai::Rhost::Mimecast`
|
24
|
+
- Update the error message patterns in the followings:
|
25
|
+
- `AuthFailure`
|
26
|
+
- `Blocked`
|
27
|
+
- `Expired`
|
28
|
+
- `MailboxFull`
|
29
|
+
- `SecurityError`
|
30
|
+
- `SpamDetected`
|
31
|
+
- `Suspend`
|
32
|
+
|
6
33
|
v5.0.1
|
7
34
|
---------------------------------------------------------------------------------------------------
|
8
35
|
- release: "Sun, 3 Mar 2024 17:17:17 +0900 (JST)"
|
data/README-JA.md
CHANGED
@@ -102,7 +102,7 @@ Sisimaiの動作環境についての詳細は[Sisimai | シシマイを使っ
|
|
102
102
|
|
103
103
|
* [Ruby 2.4.0 or later](http://www.ruby-lang.org/)
|
104
104
|
* [__oj | The fastest JSON parser and object serializer__](https://rubygems.org/gems/oj)
|
105
|
-
* Also works on [JRuby 9.
|
105
|
+
* Also works on [JRuby 9.2 or later](http://jruby.org)
|
106
106
|
* [__jrjackson | A mostly native JRuby wrapper for the java jackson json processor jar__](https://rubygems.org/gems/jrjackson)
|
107
107
|
|
108
108
|
Install
|
@@ -110,10 +110,10 @@ Install
|
|
110
110
|
### From RubyGems.org
|
111
111
|
```shell
|
112
112
|
$ sudo gem install sisimai
|
113
|
-
Fetching: sisimai-5.0.
|
114
|
-
Successfully installed sisimai-5.0.
|
115
|
-
Parsing documentation for sisimai-5.0.
|
116
|
-
Installing ri documentation for sisimai-5.0.
|
113
|
+
Fetching: sisimai-5.0.2.gem (100%)
|
114
|
+
Successfully installed sisimai-5.0.2
|
115
|
+
Parsing documentation for sisimai-5.0.2
|
116
|
+
Installing ri documentation for sisimai-5.0.2
|
117
117
|
Done installing documentation for sisimai after 6 seconds
|
118
118
|
1 gem installed
|
119
119
|
```
|
@@ -141,13 +141,13 @@ if [ -d "/usr/local/jr" ]; then \
|
|
141
141
|
...
|
142
142
|
3 gems installed
|
143
143
|
/opt/local/bin/rake install
|
144
|
-
sisimai 5.0.
|
145
|
-
sisimai (5.0.
|
144
|
+
sisimai 5.0.2 built to pkg/sisimai-5.0.2.gem.
|
145
|
+
sisimai (5.0.2) installed.
|
146
146
|
if [ -d "/usr/local/jr" ]; then \
|
147
147
|
PATH="/usr/local/jr/bin:$PATH" /usr/local/jr/bin/rake install; \
|
148
148
|
fi
|
149
|
-
sisimai 5.0.
|
150
|
-
sisimai (5.0.
|
149
|
+
sisimai 5.0.2 built to pkg/sisimai-5.0.2-java.gem.
|
150
|
+
sisimai (5.0.2) installed.
|
151
151
|
```
|
152
152
|
|
153
153
|
Usage
|
@@ -348,15 +348,15 @@ Sisimai 5.0.0から**Ruby 2.4以上**が必要になります。
|
|
348
348
|
|
349
349
|
| 機能 | Sisimai 4 | Sisimai 5 |
|
350
350
|
|------------------------------------------------------|--------------------|---------------------|
|
351
|
-
| 動作環境(CRuby) | 2.1 - | **2.4**
|
352
|
-
| 動作環境(JRuby) | 9.0.4.0 - 9.1.17.0 | 9.
|
351
|
+
| 動作環境(CRuby) | 2.1 - | **2.4** or later |
|
352
|
+
| 動作環境(JRuby) | 9.0.4.0 - 9.1.17.0 | **9.2** or later |
|
353
353
|
| 元メールファイルを操作可能なコールバック機能 | なし | あり[^3] |
|
354
354
|
| 解析エンジン(MTA/ESPモジュール)の数 | 68 | 70 |
|
355
355
|
| 検出可能なバウンス理由の数 | 29 | 34 |
|
356
356
|
| 依存Gem数(Ruby Standard Gemsを除く) | 1 Gem | 1 Gem |
|
357
|
-
| ソースコードの行数 | 10,800 行 | 11,
|
357
|
+
| ソースコードの行数 | 10,800 行 | 11,370 行 |
|
358
358
|
| テストフレームワーク | rspec | minitest |
|
359
|
-
| テスト件数(spec/またはtest/ディレクトリ) | 311,000 件 |
|
359
|
+
| テスト件数(spec/またはtest/ディレクトリ) | 311,000 件 | 338,000 件 |
|
360
360
|
| 1秒間に解析できるバウンスメール数[^4] | 231 通 | 305 通 |
|
361
361
|
| ライセンス | 2条項BSD | 2条項BSD |
|
362
362
|
| 開発会社による商用サポート | 提供中 | 提供中 |
|
data/README.md
CHANGED
@@ -99,10 +99,9 @@ System requirements
|
|
99
99
|
More details about system requirements are available at
|
100
100
|
[Sisimai | Getting Started](https://libsisimai.org/en/start/) page.
|
101
101
|
|
102
|
-
|
103
102
|
* [Ruby 2.4.0 or later](http://www.ruby-lang.org/)
|
104
103
|
* [__oj | The fastest JSON parser and object serializer__](https://rubygems.org/gems/oj)
|
105
|
-
* Also works on [JRuby 9.
|
104
|
+
* Also works on [JRuby 9.2 or later](http://jruby.org)~~
|
106
105
|
* [__jrjackson | A mostly native JRuby wrapper for the java jackson json processor jar__](https://rubygems.org/gems/jrjackson)
|
107
106
|
|
108
107
|
Install
|
@@ -110,10 +109,10 @@ Install
|
|
110
109
|
### From RubyGems
|
111
110
|
```shell
|
112
111
|
$ sudo gem install sisimai
|
113
|
-
Fetching: sisimai-5.0.
|
114
|
-
Successfully installed sisimai-5.0.
|
115
|
-
Parsing documentation for sisimai-5.0.
|
116
|
-
Installing ri documentation for sisimai-5.0.
|
112
|
+
Fetching: sisimai-5.0.2.gem (100%)
|
113
|
+
Successfully installed sisimai-5.0.2
|
114
|
+
Parsing documentation for sisimai-5.0.2
|
115
|
+
Installing ri documentation for sisimai-5.0.2
|
117
116
|
Done installing documentation for sisimai after 6 seconds
|
118
117
|
1 gem installed
|
119
118
|
```
|
@@ -141,13 +140,13 @@ if [ -d "/usr/local/jr" ]; then \
|
|
141
140
|
...
|
142
141
|
3 gems installed
|
143
142
|
/opt/local/bin/rake install
|
144
|
-
sisimai 5.0.
|
145
|
-
sisimai (5.0.
|
143
|
+
sisimai 5.0.2 built to pkg/sisimai-5.0.2.gem.
|
144
|
+
sisimai (5.0.2) installed.
|
146
145
|
if [ -d "/usr/local/jr" ]; then \
|
147
146
|
PATH="/usr/local/jr/bin:$PATH" /usr/local/jr/bin/rake install; \
|
148
147
|
fi
|
149
|
-
sisimai 5.0.
|
150
|
-
sisimai (5.0.
|
148
|
+
sisimai 5.0.2 built to pkg/sisimai-5.0.2-java.gem.
|
149
|
+
sisimai (5.0.2) installed.
|
151
150
|
```
|
152
151
|
|
153
152
|
Usage
|
@@ -348,15 +347,15 @@ Beginning with v5.0.0, Sisimai requires **Ruby 2.4.0 or later.**
|
|
348
347
|
|
349
348
|
| Features | Sisimai 4 | Sisimai 5 |
|
350
349
|
|------------------------------------------------------|--------------------|---------------------|
|
351
|
-
| System requirements (CRuby) | 2.1 - 3.3.0 | **2.4**
|
352
|
-
| System requirements (JRuby) | 9.0.4.0 - 9.1.17.0 | 9.
|
350
|
+
| System requirements (CRuby) | 2.1 - 3.3.0 | **2.4** or later |
|
351
|
+
| System requirements (JRuby) | 9.0.4.0 - 9.1.17.0 | **9.2** or later |
|
353
352
|
| Callback feature for the original email file | N/A | Available[^3] |
|
354
353
|
| The number of MTA/ESP modules | 68 | 70 |
|
355
354
|
| The number of detectable bounce reasons | 29 | 34 |
|
356
355
|
| Dependencies (Except Ruby Standard Gems) | 1 gem | 1 gem |
|
357
|
-
| Source lines of code | 10,300 lines | 11,
|
356
|
+
| Source lines of code | 10,300 lines | 11,370 lines |
|
358
357
|
| Test frameworks | rspec | minitest |
|
359
|
-
| The number of tests in spec/ or test/ directory | 311,000 tests |
|
358
|
+
| The number of tests in spec/ or test/ directory | 311,000 tests | 338,000 tests |
|
360
359
|
| The number of bounce emails decoded/sec (CRuby)[^4] | 231 emails | 305 emails |
|
361
360
|
| License | 2 Clause BSD | 2 Caluse BSD |
|
362
361
|
| Commercial support | Available | Available |
|
data/lib/sisimai/fact.rb
CHANGED
@@ -46,6 +46,18 @@ module Sisimai
|
|
46
46
|
RFC822Head = Sisimai::RFC5322.HEADERFIELDS(:all)
|
47
47
|
ActionList = { delayed: 1, delivered: 1, expanded: 1, failed: 1, relayed: 1 };
|
48
48
|
|
49
|
+
if RUBY_PLATFORM.start_with?('java')
|
50
|
+
# [WORKAROUND] #159 #267 JRuby seems to fail and throws exception at strptime(), but this
|
51
|
+
# issue might be fixed in a future version of JRuby.
|
52
|
+
# https://gist.github.com/hiroyuki-sato/6ef40245874d4c847a95ef99886e4fa7
|
53
|
+
# https://github.com/sisimai/rb-sisimai/issues/267#issuecomment-1976642884
|
54
|
+
# https://github.com/jruby/jruby/issues/8139
|
55
|
+
# https://github.com/sisimai/rb-sisimai/issues/267
|
56
|
+
TimeModule = ::DateTime
|
57
|
+
else
|
58
|
+
TimeModule = Sisimai::Time
|
59
|
+
end
|
60
|
+
|
49
61
|
# Constructor of Sisimai::Fact
|
50
62
|
# @param [Hash] argvs Including each parameter
|
51
63
|
# @return [Sisimai::Fact] Structured email data
|
@@ -188,7 +200,7 @@ module Sisimai
|
|
188
200
|
|
189
201
|
begin
|
190
202
|
# Convert from the date string to an object then calculate time zone offset.
|
191
|
-
t =
|
203
|
+
t = TimeModule.strptime(datestring, '%a, %d %b %Y %T')
|
192
204
|
p['timestamp'] = (t.to_time.to_i - zoneoffset) || nil
|
193
205
|
rescue
|
194
206
|
warn ' ***warning: Failed to strptime ' << datestring.to_s
|
@@ -196,12 +208,12 @@ module Sisimai
|
|
196
208
|
next unless p['timestamp']
|
197
209
|
|
198
210
|
# OTHER_TEXT_HEADERS:
|
199
|
-
|
200
|
-
unless
|
201
|
-
# Get localhost and remote host name from Received header.
|
202
|
-
|
203
|
-
|
204
|
-
|
211
|
+
rr = mesg1['header']['received'] || []
|
212
|
+
unless rr.empty?
|
213
|
+
# Get a localhost and a remote host name from Received header.
|
214
|
+
p['rhost'] = Sisimai::RFC5322.received(rr[-1])[1] if p['rhost'].empty?
|
215
|
+
p['lhost'] = '' if p['rhost'] == p['lhost']
|
216
|
+
p['lhost'] = Sisimai::RFC5322.received(rr[ 0])[0] if p['lhost'].empty?
|
205
217
|
end
|
206
218
|
|
207
219
|
# Remove square brackets and curly brackets from the host variable
|
@@ -339,9 +351,35 @@ module Sisimai
|
|
339
351
|
o['catch'] = p['catch'] || nil
|
340
352
|
o['hardbounce'] = p['hardbounce']
|
341
353
|
o['replycode'] = Sisimai::SMTP::Reply.find(p['diagnosticcode']).to_s if o['replycode'].empty?
|
342
|
-
o['timestamp'] =
|
354
|
+
o['timestamp'] = TimeModule.parse(::Time.at(p['timestamp']).to_s)
|
343
355
|
o['timezoneoffset'] = p['timezoneoffset'] || '+0000'
|
344
356
|
|
357
|
+
# ALIAS
|
358
|
+
while true do
|
359
|
+
# Look up the Envelope-To address from the Received: header in the original message
|
360
|
+
# when the recipient address is same with the value of o['alias'].
|
361
|
+
break if o['alias'].empty?
|
362
|
+
break if o['recipient'].address != o['alias']
|
363
|
+
break unless rfc822data.has_key?('received')
|
364
|
+
break if rfc822data['received'].empty?
|
365
|
+
|
366
|
+
rfc822data['received'].reverse.each do |er|
|
367
|
+
# Search for the string " for " from the Received: header
|
368
|
+
next unless er.include?(' for ')
|
369
|
+
|
370
|
+
af = Sisimai::RFC5322.received(er)
|
371
|
+
next if af.empty?
|
372
|
+
next if af[5].empty?
|
373
|
+
next unless Sisimai::Address.is_emailaddress(af[5])
|
374
|
+
next if o['recipient'].address == af[5]
|
375
|
+
|
376
|
+
o['alias'] = af[5]
|
377
|
+
break
|
378
|
+
end
|
379
|
+
break
|
380
|
+
end
|
381
|
+
o['alias'] = '' if o['alias'] == o['recipient'].address
|
382
|
+
|
345
383
|
# REASON: Decide the reason of email bounce
|
346
384
|
if o['reason'].empty? || RetryIndex[o['reason']]
|
347
385
|
# The value of "reason" is empty or is needed to check with other values again
|
@@ -305,7 +305,6 @@ module Sisimai::Lhost
|
|
305
305
|
|
306
306
|
dscontents.each do |e|
|
307
307
|
# Set default values if each value is empty.
|
308
|
-
e['lhost'] ||= permessage['rhost']
|
309
308
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
310
309
|
|
311
310
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].to_s.tr("\n", ' '))
|
@@ -98,7 +98,6 @@ module Sisimai::Lhost
|
|
98
98
|
|
99
99
|
dscontents.each do |e|
|
100
100
|
# Set default values if each value is empty.
|
101
|
-
e['lhost'] ||= permessage['rhost']
|
102
101
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
103
102
|
|
104
103
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
data/lib/sisimai/lhost/aol.rb
CHANGED
@@ -100,7 +100,6 @@ module Sisimai::Lhost
|
|
100
100
|
|
101
101
|
dscontents.each do |e|
|
102
102
|
# Set default values if each value is empty.
|
103
|
-
e['lhost'] ||= permessage['rhost']
|
104
103
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
105
104
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].tr("\n", ' '))
|
106
105
|
|
@@ -108,7 +108,6 @@ module Sisimai::Lhost
|
|
108
108
|
|
109
109
|
dscontents.each do |e|
|
110
110
|
# Set default values if each value is empty.
|
111
|
-
e['lhost'] ||= permessage['rhost']
|
112
111
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
113
112
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
114
113
|
e['command'] = thecommand || ''
|
data/lib/sisimai/lhost/domino.rb
CHANGED
@@ -123,7 +123,6 @@ module Sisimai::Lhost
|
|
123
123
|
dscontents.each do |e|
|
124
124
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
125
125
|
e['recipient'] = Sisimai::Address.s3s4(e['recipient'])
|
126
|
-
e['lhost'] ||= permessage['rhost']
|
127
126
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
128
127
|
|
129
128
|
MessagesOf.each_key do |r|
|
@@ -147,7 +147,7 @@ module Sisimai::Lhost
|
|
147
147
|
next unless p > 0
|
148
148
|
|
149
149
|
# #550 5.1.1 RESOLVER.ADR.RecipNotFound; not found ##
|
150
|
-
f = e['diagnosis'][p + 1, e['diagnosis'].index(';') - p -1]
|
150
|
+
f = e['diagnosis'][p + 1, e['diagnosis'].index(';') - p - 1]
|
151
151
|
NDRSubject.each_key do |r|
|
152
152
|
# Try to match with error subject strings
|
153
153
|
next unless f == r
|
data/lib/sisimai/lhost/exim.rb
CHANGED
@@ -147,7 +147,6 @@ module Sisimai::Lhost
|
|
147
147
|
readcursor = 0 # (Integer) Points the current cursor position
|
148
148
|
nextcursor = 0
|
149
149
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
150
|
-
localhost0 = '' # (String) Local MTA
|
151
150
|
boundary00 = '' # (String) Boundary string
|
152
151
|
v = nil
|
153
152
|
|
@@ -342,18 +341,13 @@ module Sisimai::Lhost
|
|
342
341
|
end
|
343
342
|
return nil unless recipients > 0
|
344
343
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
p2 = mhead['received'][-1].index(' ', p1 + 5) || -1
|
350
|
-
localhost0 = mhead['received'][-1][p1 + 5, p2 - p1 - 5] if p1 > -1 && p2 > -1
|
351
|
-
end
|
344
|
+
# Get the name of the local MTA
|
345
|
+
# Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
|
346
|
+
receivedby = mhead['received'] || []
|
347
|
+
recvdtoken = Sisimai::RFC5322.received(receivedby[-1])
|
352
348
|
|
353
349
|
dscontents.each do |e|
|
354
|
-
#
|
355
|
-
e['lhost'] ||= localhost0
|
356
|
-
|
350
|
+
# Check the error message, the rhost, the lhost, and the smtp command.
|
357
351
|
unless e['diagnosis']
|
358
352
|
# Empty Diagnostic-Code: or error message
|
359
353
|
unless boundary00.empty?
|
@@ -405,12 +399,9 @@ module Sisimai::Lhost
|
|
405
399
|
p1 = e['diagnosis'].index('host ') || -1
|
406
400
|
p2 = e['diagnosis'].index(' ', p1 + 5) || -1
|
407
401
|
e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
|
408
|
-
|
409
|
-
unless e['rhost']
|
410
|
-
# Get localhost and remote host name from Received header.
|
411
|
-
e['rhost'] = Sisimai::RFC5322.received(mhead['received'][-1]).pop unless mhead['received'].empty?
|
412
|
-
end
|
402
|
+
e['rhost'] ||= recvdtoken[1]
|
413
403
|
end
|
404
|
+
e['lhost'] ||= recvdtoken[0]
|
414
405
|
|
415
406
|
unless e['command']
|
416
407
|
# Get the SMTP command name for the session
|
@@ -48,9 +48,10 @@ module Sisimai::Lhost
|
|
48
48
|
# * You may need to join the group before receiving permission to post.
|
49
49
|
# * This group may not be open to posting.
|
50
50
|
entiremesg = emailparts[0].split(/\n\n/, 5).slice(0, 4).join(' ').tr("\n", ' ');
|
51
|
+
receivedby = mhead['received'] || []
|
51
52
|
recordwide['diagnosis'] = Sisimai::String.sweep(entiremesg)
|
52
53
|
recordwide['reason'] = emailparts[0].scan(/^[ ]?[*][ ]?/).size == 4 ? 'rejected' : 'onhold'
|
53
|
-
recordwide['rhost'] = Sisimai::RFC5322.received(
|
54
|
+
recordwide['rhost'] = Sisimai::RFC5322.received(receivedby[0])[1]
|
54
55
|
|
55
56
|
mhead['x-failed-recipients'].split(',').each do |e|
|
56
57
|
# X-Failed-Recipients: neko@example.jp, nyaan@example.org, ...
|
data/lib/sisimai/lhost/gsuite.rb
CHANGED
data/lib/sisimai/lhost/mailru.rb
CHANGED
@@ -69,7 +69,6 @@ module Sisimai::Lhost
|
|
69
69
|
bodyslices = emailparts[0].split("\n")
|
70
70
|
readcursor = 0 # (Integer) Points the current cursor position
|
71
71
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
72
|
-
localhost0 = '' # (String) Local MTA
|
73
72
|
v = nil
|
74
73
|
|
75
74
|
while e = bodyslices.shift do
|
@@ -145,18 +144,13 @@ module Sisimai::Lhost
|
|
145
144
|
end
|
146
145
|
return nil unless recipients > 0
|
147
146
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
p2 = mhead['received'][-1].index(' ', p1 + 5) || -1
|
153
|
-
localhost0 = mhead['received'][-1][p1 + 5, p2 - p1 - 5] if p1 > -1
|
154
|
-
end
|
147
|
+
# Get the name of the local MTA
|
148
|
+
# Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
|
149
|
+
receivedby = mhead['received'] || []
|
150
|
+
recvdtoken = Sisimai::RFC5322.received(receivedby[-1])
|
155
151
|
|
156
152
|
dscontents.each do |e|
|
157
|
-
#
|
158
|
-
e['lhost'] ||= localhost0
|
159
|
-
|
153
|
+
# Check the error message, the rhost, the lhost, and the smtp command.
|
160
154
|
unless e['alterrors'].to_s.empty?
|
161
155
|
# Copy alternative error message
|
162
156
|
e['diagnosis'] ||= e['alterrors']
|
@@ -175,13 +169,10 @@ module Sisimai::Lhost
|
|
175
169
|
# host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
|
176
170
|
p1 = e['diagnosis'].index('host ') || -1
|
177
171
|
p2 = e['diagnosis'].index(' ', p1 + 5) || -1
|
178
|
-
e['rhost']
|
179
|
-
|
180
|
-
unless e['rhost']
|
181
|
-
# Get localhost and remote host name from Received header.
|
182
|
-
e['rhost'] = Sisimai::RFC5322.received(mhead['received'][-1]).pop unless mhead['received'].empty?
|
183
|
-
end
|
172
|
+
e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
|
173
|
+
e['rhost'] ||= recvdtoken[1]
|
184
174
|
end
|
175
|
+
e['lhost'] ||= recvdtoken[0]
|
185
176
|
|
186
177
|
unless e['command']
|
187
178
|
# Get the SMTP command name for the session
|
@@ -98,7 +98,6 @@ module Sisimai::Lhost
|
|
98
98
|
|
99
99
|
dscontents.each do |e|
|
100
100
|
# Set default values if each value is empty.
|
101
|
-
e['lhost'] ||= permessage['rhost']
|
102
101
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
103
102
|
e['command'] = commandset.shift || ''
|
104
103
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
@@ -104,7 +104,7 @@ module Sisimai::Lhost
|
|
104
104
|
rhosts = Sisimai::RFC5322.received(rheads[-1])
|
105
105
|
|
106
106
|
e['lhost'] ||= Sisimai::RFC5322.received(rheads[0]).shift
|
107
|
-
|
107
|
+
[rhosts[0], rhosts[1]].each do |ee|
|
108
108
|
# Avoid "... by m-FILTER"
|
109
109
|
next unless ee.include?('.')
|
110
110
|
e['rhost'] = ee
|
@@ -89,7 +89,6 @@ module Sisimai::Lhost
|
|
89
89
|
bodyslices = emailparts[0].split("\n")
|
90
90
|
readcursor = 0 # (Integer) Points the current cursor position
|
91
91
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
92
|
-
localhost0 = '' # (String) Local MTA
|
93
92
|
v = nil
|
94
93
|
|
95
94
|
while e = bodyslices.shift do
|
@@ -135,19 +134,14 @@ module Sisimai::Lhost
|
|
135
134
|
end
|
136
135
|
return nil unless recipients > 0
|
137
136
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
if (p1 + 1) * (p2 + 1) > 0
|
144
|
-
# Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
|
145
|
-
localhost0 = mhead['received'][-1][p1 + 5, p2 - p1 - 5]
|
146
|
-
end
|
147
|
-
end
|
137
|
+
# Get the name of the local MTA
|
138
|
+
# Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
|
139
|
+
receivedby = mhead['received'] || []
|
140
|
+
recvdtoken = Sisimai::RFC5322.received(receivedby[-1])
|
148
141
|
|
149
142
|
dscontents.each do |e|
|
150
|
-
|
143
|
+
# Check the error message, the rhost, the lhost, and the smtp command.
|
144
|
+
e['lhost'] = recvdtoken[0]
|
151
145
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].gsub(/[-]{2}.*\z/, ''))
|
152
146
|
|
153
147
|
unless e['rhost']
|
@@ -156,12 +150,8 @@ module Sisimai::Lhost
|
|
156
150
|
p2 = e['diagnosis'].index(' ', p1 + 5)
|
157
151
|
|
158
152
|
# host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
|
159
|
-
e['rhost']
|
160
|
-
|
161
|
-
unless e['rhost']
|
162
|
-
# Get localhost and remote host name from Received header.
|
163
|
-
e['rhost'] = Sisimai::RFC5322.received(mhead['received'][-1]).pop unless mhead['received'].empty?
|
164
|
-
end
|
153
|
+
e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
|
154
|
+
e['rhost'] ||= recvdtoken[1]
|
165
155
|
end
|
166
156
|
|
167
157
|
unless e['command']
|
@@ -70,7 +70,7 @@ module Sisimai::Lhost
|
|
70
70
|
%r/\A5[.]7[.]1[23]\z/ => 'rejected',
|
71
71
|
%r/\A5[.]7[.]124\z/ => 'rejected',
|
72
72
|
%r/\A5[.]7[.]13[3-6]\z/ => 'rejected',
|
73
|
-
%r/\A5[.]7[.]23\z/ => '
|
73
|
+
%r/\A5[.]7[.]23\z/ => 'authfailure',
|
74
74
|
%r/\A5[.]7[.]25\z/ => 'networkerror',
|
75
75
|
%r/\A5[.]7[.]50[1-3]\z/ => 'spamdetected',
|
76
76
|
%r/\A5[.]7[.]50[4-5]\z/ => 'filtered',
|
@@ -97,7 +97,6 @@ module Sisimai::Lhost
|
|
97
97
|
|
98
98
|
dscontents.each do |e|
|
99
99
|
# Set default values if each value is empty.
|
100
|
-
e['lhost'] ||= permessage['rhost']
|
101
100
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
102
101
|
|
103
102
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
|
@@ -96,7 +96,6 @@ module Sisimai::Lhost
|
|
96
96
|
|
97
97
|
dscontents.each do |e|
|
98
98
|
# Set default values if each value is empty.
|
99
|
-
e['lhost'] ||= permessage['rhost']
|
100
99
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
101
100
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].tr("\n", ' '))
|
102
101
|
|
@@ -124,6 +124,7 @@ module Sisimai::Lhost
|
|
124
124
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
125
125
|
e['replycode'] = Sisimai::SMTP::Reply.find(e['diagnosis']) || ''
|
126
126
|
e['status'] = e['replycode'][0, 1] + '.0.0' if e['replycode'].size == 3
|
127
|
+
e['command'] = thecommand
|
127
128
|
|
128
129
|
if e['status'] == '5.0.0' || e['status'] == '4.0.0'
|
129
130
|
# Get the value of D.S.N. from the error message or the value of Diagnostic-Code header.
|
@@ -138,9 +139,6 @@ module Sisimai::Lhost
|
|
138
139
|
e['status'] = Sisimai::SMTP::Status.code('expired') || e['status']
|
139
140
|
end
|
140
141
|
end
|
141
|
-
|
142
|
-
e['lhost'] ||= permessage['rhost']
|
143
|
-
e['command'] = thecommand
|
144
142
|
end
|
145
143
|
|
146
144
|
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
@@ -168,7 +168,6 @@ module Sisimai::Lhost
|
|
168
168
|
dscontents.each do |e|
|
169
169
|
# Set default values if each value is empty.
|
170
170
|
e['diagnosis'] ||= ''
|
171
|
-
e['lhost'] ||= permessage['rhost']
|
172
171
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
173
172
|
|
174
173
|
if anotherset['diagnosis']
|
data/lib/sisimai/lhost/yandex.rb
CHANGED
@@ -103,7 +103,6 @@ module Sisimai::Lhost
|
|
103
103
|
|
104
104
|
dscontents.each do |e|
|
105
105
|
# Set default values if each value is empty.
|
106
|
-
e['lhost'] ||= permessage['rhost']
|
107
106
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
108
107
|
|
109
108
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].tr("\n", ' '))
|
data/lib/sisimai/message.rb
CHANGED
@@ -198,17 +198,26 @@ module Sisimai
|
|
198
198
|
# Select and convert all the headers in $argv0. The following regular expression is based on
|
199
199
|
# https://gist.github.com/xtetsuji/b080e1f5551d17242f6415aba8a00239
|
200
200
|
headermaps = { 'subject' => '' }
|
201
|
-
|
201
|
+
receivedby = []
|
202
202
|
argv0.scan(/^([\w-]+):[ ]*(.*?)\n(?![\s\t])/m) { |e| headermaps[e[0].downcase] = e[1] }
|
203
203
|
headermaps.delete('received')
|
204
204
|
headermaps.each_key { |e| headermaps[e].gsub!(/\n[\s\t]+/, ' ') }
|
205
205
|
|
206
206
|
if argv0.include?('Received:')
|
207
207
|
# Capture values of each Received: header
|
208
|
-
|
209
|
-
|
208
|
+
re = argv0.scan(/^Received:[ ]*(.*?)\n(?![\s\t])/m).flatten
|
209
|
+
re.each do |e|
|
210
|
+
# 1. Exclude the Received header including "(qmail ** invoked from network)".
|
211
|
+
# 2. Convert all consecutive spaces and line breaks into a single space character.
|
212
|
+
next if e.include?(' invoked by uid')
|
213
|
+
next if e.include?(' invoked from network')
|
214
|
+
|
215
|
+
e.gsub!(/\n[\s\t]+/, ' ')
|
216
|
+
e.squeeze!("\n\t ")
|
217
|
+
receivedby << e
|
218
|
+
end
|
210
219
|
end
|
211
|
-
headermaps['received'] =
|
220
|
+
headermaps['received'] = receivedby
|
212
221
|
|
213
222
|
return headermaps unless argv1
|
214
223
|
return headermaps if headermaps['subject'].empty?
|
@@ -46,6 +46,7 @@ module Sisimai
|
|
46
46
|
'part of their network is on our block list',
|
47
47
|
'please use the smtp server of your isp',
|
48
48
|
'refused - see http',
|
49
|
+
'rejected - multi-blacklist', # junkemailfilter.com
|
49
50
|
'rejected because the sending mta or the sender has not passed validation',
|
50
51
|
'rejecting open proxy', # Sendmail(srvrsmtp.c)
|
51
52
|
'sender ip address rejected',
|
@@ -78,6 +79,7 @@ module Sisimai
|
|
78
79
|
['message from ', ' rejected based on blacklist'],
|
79
80
|
['messages from ', ' temporarily deferred due to user complaints'], # Yahoo!
|
80
81
|
['server ip ', ' listed as abusive'],
|
82
|
+
['sorry! your ip address', ' is blocked by rbl'], # junkemailfilter.com
|
81
83
|
['the domain ', ' is blacklisted'],
|
82
84
|
['the email ', ' is blacklisted'],
|
83
85
|
['the ip', ' is blacklisted'],
|
@@ -18,6 +18,7 @@ module Sisimai
|
|
18
18
|
'has been delayed',
|
19
19
|
'it has not been collected after',
|
20
20
|
'message expired after sitting in queue for',
|
21
|
+
'message expired, cannot connect to remote server',
|
21
22
|
'message expired, connection refulsed',
|
22
23
|
'message timed out',
|
23
24
|
'retry time not reached for any host after a long failure period',
|
@@ -29,6 +29,7 @@ module Sisimai
|
|
29
29
|
'insecure mail relay',
|
30
30
|
'recipient address rejected: access denied',
|
31
31
|
"sorry, you don't authenticate or the domain isn't in my list of allowed rcpthosts",
|
32
|
+
'starttls is required to send mail',
|
32
33
|
'tls required but not supported', # SendGrid:the recipient mailserver does not support TLS or have a valid certificate
|
33
34
|
'unauthenticated senders not allowed',
|
34
35
|
'verification failure',
|
@@ -76,6 +76,7 @@ module Sisimai
|
|
76
76
|
'spam score ',
|
77
77
|
'spambouncer identified spam', # SpamBouncer identified SPAM
|
78
78
|
'spamming not allowed',
|
79
|
+
'too many spam complaints',
|
79
80
|
'too much spam.', # Earthlink
|
80
81
|
'the email message was detected as spam',
|
81
82
|
'the message has been rejected by spam filtering engine',
|
@@ -21,6 +21,7 @@ module Sisimai
|
|
21
21
|
'recipient rejected: temporarily inactive',
|
22
22
|
'recipient suspend the service',
|
23
23
|
'this account has been disabled or discontinued',
|
24
|
+
'this address no longer accepts mail',
|
24
25
|
'this mailbox is disabled',
|
25
26
|
'user suspended', # http://mail.163.com/help/help_spam_16.htm
|
26
27
|
'vdelivermail: account is locked email bounced',
|
data/lib/sisimai/rfc5322.rb
CHANGED
@@ -3,6 +3,7 @@ module Sisimai
|
|
3
3
|
module RFC5322
|
4
4
|
class << self
|
5
5
|
require 'sisimai/string'
|
6
|
+
require 'sisimai/address'
|
6
7
|
HeaderTable = {
|
7
8
|
:messageid => %w[message-id],
|
8
9
|
:subject => %w[subject],
|
@@ -52,79 +53,134 @@ module Sisimai
|
|
52
53
|
# @return [Array] Received header as a structured data
|
53
54
|
def received(argv1)
|
54
55
|
return [] unless argv1.is_a?(::String)
|
56
|
+
return [] if argv1.include?(' invoked by uid')
|
57
|
+
return [] if argv1.include?(' invoked from network')
|
58
|
+
|
59
|
+
# - https://datatracker.ietf.org/doc/html/rfc5322
|
60
|
+
# received = "Received:" *received-token ";" date-time CRLF
|
61
|
+
# received-token = word / angle-addr / addr-spec / domain
|
62
|
+
#
|
63
|
+
# - Appendix A.4. Message with Trace Fields
|
64
|
+
# Received:
|
65
|
+
# from x.y.test
|
66
|
+
# by example.net
|
67
|
+
# via TCP
|
68
|
+
# with ESMTP
|
69
|
+
# id ABC12345
|
70
|
+
# for <mary@example.net>; 21 Nov 1997 10:05:43 -0600
|
71
|
+
recvd = argv1.split(' ')
|
72
|
+
label = %w[from by via with id for]
|
73
|
+
token = {}
|
74
|
+
other = []
|
75
|
+
alter = []
|
76
|
+
right = false
|
77
|
+
range = recvd.size
|
78
|
+
index = -1
|
79
|
+
|
80
|
+
recvd.each do |e|
|
81
|
+
# Look up each label defined in "label" from Received header
|
82
|
+
index += 1
|
83
|
+
break unless index < range; f = e.downcase
|
84
|
+
next unless label.any? { |a| f == a }
|
85
|
+
token[f] = recvd[index + 1] || next
|
86
|
+
token[f] = token[f].downcase.delete('();')
|
87
|
+
|
88
|
+
next unless f == 'from'
|
89
|
+
break unless index + 2 < range
|
90
|
+
next unless recvd[index + 2].start_with?('(')
|
91
|
+
|
92
|
+
# Get and keep a hostname in the comment as follows:
|
93
|
+
# from mx1.example.com (c213502.kyoto.example.ne.jp [192.0.2.135]) by mx.example.jp (V8/cf)
|
94
|
+
# [
|
95
|
+
# "from", # index + 0
|
96
|
+
# "mx1.example.com", # index + 1
|
97
|
+
# "(c213502.kyoto.example.ne.jp", # index + 2
|
98
|
+
# "[192.0.2.135])", # index + 3
|
99
|
+
# "by",
|
100
|
+
# "mx.example.jp",
|
101
|
+
# "(V8/cf)",
|
102
|
+
# ...
|
103
|
+
# ]
|
104
|
+
# The 2nd element after the current element is NOT a continuation of the current element
|
105
|
+
# such as "(c213502.kyoto.example.ne.jp)"
|
106
|
+
other << recvd[index + 2].delete('();')
|
107
|
+
|
108
|
+
# The 2nd element after the current element is a continuation of the current element.
|
109
|
+
# such as "(c213502.kyoto.example.ne.jp", "[192.0.2.135])"
|
110
|
+
break unless index + 3 < range
|
111
|
+
other << recvd[index + 3].delete('();')
|
112
|
+
end
|
55
113
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# Wed, 26 Feb 2014 06:05:48 -0500
|
69
|
-
value['from'] = Sisimai::String.sweep(argv1[p1 + 5, p2 - p1 - 5])
|
70
|
-
value['by'] = Sisimai::String.sweep(argv1[p2 + 3, p3 - p2 - 3])
|
114
|
+
other.each do |e|
|
115
|
+
# Check alternatives in "other", and then delete uninformative values.
|
116
|
+
next if e.nil?
|
117
|
+
next if e.size < 4
|
118
|
+
next if e == 'unknown'
|
119
|
+
next if e == 'localhost'
|
120
|
+
next if e == '[127.0.0.1]'
|
121
|
+
next if e == '[IPv6:::1]'
|
122
|
+
next unless e.include?('.')
|
123
|
+
next if e.include?('=')
|
124
|
+
alter << e
|
125
|
+
end
|
71
126
|
|
72
|
-
|
73
|
-
#
|
74
|
-
|
75
|
-
|
127
|
+
%w[from by].each do |e|
|
128
|
+
# Remove square brackets from the IP address such as "[192.0.2.25]"
|
129
|
+
next if token[e].nil?
|
130
|
+
next if token[e].empty?
|
131
|
+
next unless token[e].start_with?('[')
|
132
|
+
token[e] = Sisimai::String.ipv4(token[e]).shift || ''
|
76
133
|
end
|
134
|
+
token['from'] ||= ''
|
77
135
|
|
78
|
-
|
79
|
-
#
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
hostname = ''
|
85
|
-
hostaddr = ''
|
86
|
-
|
87
|
-
while e = received.shift do
|
88
|
-
# Received: from [10.22.22.222] (smtp-gateway.kyoto.ocn.ne.jp [192.0.2.222])
|
89
|
-
cv = Sisimai::String.ipv4(e) || []
|
90
|
-
if cv.size > 0
|
91
|
-
# [192.0.2.1] or (192.0.2.1)
|
92
|
-
addrlist.append(*cv)
|
93
|
-
else
|
94
|
-
# hostname
|
95
|
-
e = e.delete('()').strip
|
96
|
-
namelist << e
|
97
|
-
end
|
98
|
-
end
|
136
|
+
while true do
|
137
|
+
# Prefer hostnames over IP addresses, except for localhost.localdomain and similar.
|
138
|
+
break if token['from'] == 'localhost'
|
139
|
+
break if token['from'] == 'localhost.localdomain'
|
140
|
+
break unless token['from'].include?('.') # A hostname without a domain name
|
141
|
+
break unless Sisimai::String.ipv4(token['from']).empty?
|
99
142
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
break
|
105
|
-
end
|
143
|
+
# No need to rewrite token['from']
|
144
|
+
right = true
|
145
|
+
break
|
146
|
+
end
|
106
147
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
148
|
+
while true do
|
149
|
+
# Try to rewrite uninformative hostnames and IP addresses in token['from']
|
150
|
+
break if right # There is no need to rewrite
|
151
|
+
break if alter.empty? # There is no alternative to rewriting
|
152
|
+
break if alter[0].include?(token['from'])
|
153
|
+
|
154
|
+
if token['from'].start_with?('localhost')
|
155
|
+
# localhost or localhost.localdomain
|
156
|
+
token['from'] = alter[0]
|
157
|
+
elsif token['from'].index('.')
|
158
|
+
# A hostname without a domain name such as "mail", "mx", or "mbox"
|
159
|
+
token['from'] = alter[0] if alter[0].include?('.')
|
160
|
+
else
|
161
|
+
# An IPv4 address
|
162
|
+
token['from'] = alter[0]
|
116
163
|
end
|
117
|
-
|
118
|
-
value['from'] = hostname || hostaddr || addrlist[-1]
|
164
|
+
break
|
119
165
|
end
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
166
|
+
token.delete('from') if token['from'].nil?
|
167
|
+
token.delete('by') if token['by'].nil?
|
168
|
+
token['for'] = Sisimai::Address.s3s4(token['for']) if token.has_key?('for')
|
169
|
+
|
170
|
+
token.keys.each do |e|
|
171
|
+
# Delete an invalid value
|
172
|
+
token[e] = '' if token[e].include?(' ')
|
173
|
+
token[e].delete!('[]') # Remove "[]" from the IP address
|
126
174
|
end
|
127
|
-
|
175
|
+
|
176
|
+
return [
|
177
|
+
token['from'] || '',
|
178
|
+
token['by'] || '',
|
179
|
+
token['via'] || '',
|
180
|
+
token['with'] || '',
|
181
|
+
token['id'] || '',
|
182
|
+
token['for'] || '',
|
183
|
+
]
|
128
184
|
end
|
129
185
|
|
130
186
|
# Split given entire message body into error message lines and the original message part only
|
@@ -7,6 +7,7 @@ module Sisimai
|
|
7
7
|
class << self
|
8
8
|
MessagesOf = {
|
9
9
|
# https://community.mimecast.com/s/article/Mimecast-SMTP-Error-Codes-842605754
|
10
|
+
# https://community.mimecast.com/s/article/email-security-cloud-gateway-mimecast-smtp-error-codes
|
10
11
|
'authfailure' => [
|
11
12
|
# - The inbound message has been rejected because the originated IP address isn't list-
|
12
13
|
# ed in the published SPF records for the sending domain.
|
@@ -76,6 +77,12 @@ module Sisimai
|
|
76
77
|
[554, 'maximum email size exceeded'],
|
77
78
|
],
|
78
79
|
'networkerror' => [
|
80
|
+
# - The recipients' domains have MX records configured incorrectly
|
81
|
+
# - Check and remove any MX records that point to hostnames with outbound references.
|
82
|
+
# Only Inbound smart hosts are supported on MX records.
|
83
|
+
[451, 'the incorrect hostname used for inbounds'],
|
84
|
+
[550, 'the incorrect hostname used for inbounds'],
|
85
|
+
|
79
86
|
# - The message has too many "received headers" as it has been forwarded across multi-
|
80
87
|
# ple hops. Once 25 hops have been reached, the email is rejected.
|
81
88
|
# - Investigate the email addresses in the communication pairs, to see what forwarders
|
@@ -135,8 +142,8 @@ module Sisimai
|
|
135
142
|
|
136
143
|
# - A personal block policy is in place for the email address/domain.
|
137
144
|
# - Remove the email address/domain from the Managed Senders list.
|
138
|
-
[550, 'envelope blocked
|
139
|
-
[550, 'envelope blocked
|
145
|
+
[550, 'envelope blocked - user entry'],
|
146
|
+
[550, 'envelope blocked - user domain entry'],
|
140
147
|
[550, 'rejected by header-based manually blocked senders - block for manual block'],
|
141
148
|
|
142
149
|
# - A Block Sender Policy has been applied to reject emails based on the Header From or
|
data/lib/sisimai/version.rb
CHANGED
@@ -0,0 +1,85 @@
|
|
1
|
+
Return-Path: <>
|
2
|
+
X-Original-To: nekochan@email.example.jp
|
3
|
+
Delivered-To: mail@email.example.jp
|
4
|
+
Received: from mx311.ume.example.ne.jp (ip-192-0-2-25.us-east-1.compute.internal [192.0.2.25])
|
5
|
+
by mx1.email.example.jp (Postfix) with ESMTPS id Ln2ZS7LPwxzW4HMF
|
6
|
+
for <nekochan@email.example.jp>; Wed, 7 Feb 2024 23:34:45 +0900 (JST)
|
7
|
+
Received: from localhost (localhost)
|
8
|
+
by mx311.ume.example.ne.jp (8.16.1/8.16.1) id 3LGub1et091679;
|
9
|
+
Wed, 7 Feb 2024 23:34:45 +0900 (JST)
|
10
|
+
(envelope-from MAILER-DAEMON)
|
11
|
+
Date: Wed, 7 Feb 2024 23:34:45 +0900 (JST)
|
12
|
+
From: Mail Delivery Subsystem <MAILER-DAEMON@mx311.ume.example.ne.jp>
|
13
|
+
To: <nekochan@email.example.jp>
|
14
|
+
Message-Id: <202402072222.3LGub1et091679@mx311.ume.example.ne.jp>
|
15
|
+
MIME-Version: 1.0
|
16
|
+
Content-Type: multipart/report; report-type=delivery-status;
|
17
|
+
boundary="3LGub1et091679.1157497350/mx311.ume.example.ne.jp"
|
18
|
+
Subject: Returned mail: see transcript for details
|
19
|
+
Auto-Submitted: auto-generated (failure)
|
20
|
+
|
21
|
+
This is a MIME-encapsulated message
|
22
|
+
|
23
|
+
--3LGub1et091679.1157497350/mx311.ume.example.ne.jp
|
24
|
+
|
25
|
+
The original message was received at Wed, 7 Feb 2024 23:34:45 +0900 (JST)
|
26
|
+
from localhost [127.0.0.1]
|
27
|
+
|
28
|
+
----- The following addresses had permanent fatal errors -----
|
29
|
+
<kijitora-cat@google.example.com>
|
30
|
+
(reason: 550-5.7.26 The MAIL FROM domain [email.example.jp] has an SPF record with a hard fail)
|
31
|
+
|
32
|
+
----- Transcript of session follows -----
|
33
|
+
... while talking to gmail-smtp-in.l.google.com.:
|
34
|
+
>>> DATA
|
35
|
+
<<< 550-5.7.26 The MAIL FROM domain [email.example.jp] has an SPF record with a hard fail
|
36
|
+
<<< 550-5.7.26 policy (-all) but it fails to pass SPF checks with the ip:
|
37
|
+
<<< 550-5.7.26 [203.0.113.22]. To best protect our users from spam and phishing,
|
38
|
+
<<< 550-5.7.26 the message has been blocked. For instructions on setting up
|
39
|
+
<<< 550-5.7.26 authentication, go to
|
40
|
+
<<< 550 5.7.26 https://support.google.com/mail/answer/81126#authentication q2.22 - gsmtp
|
41
|
+
554 5.0.0 Service unavailable
|
42
|
+
|
43
|
+
--3LGub1et091679.1157497350/mx311.ume.example.ne.jp
|
44
|
+
Content-Type: message/delivery-status
|
45
|
+
|
46
|
+
Reporting-MTA: dns; mx311.ume.example.ne.jp
|
47
|
+
Arrival-Date: Wed, 7 Feb 2024 23:34:45 +0900 (JST)
|
48
|
+
|
49
|
+
Final-Recipient: RFC822; kijitora-cat@google.example.com
|
50
|
+
X-Actual-Recipient: rfc822; kijitora-cat@google.example.com
|
51
|
+
Action: failed
|
52
|
+
Status: 5.7.26
|
53
|
+
Remote-MTA: DNS; gmail-smtp-in.l.google.com
|
54
|
+
Diagnostic-Code: SMTP; 550-5.7.26 The MAIL FROM domain [email.example.jp] has an SPF record with a hard fail
|
55
|
+
Last-Attempt-Date: Wed, 7 Feb 2024 23:34:45 +0900 (JST)
|
56
|
+
|
57
|
+
--3LGub1et091679.1157497350/mx311.ume.example.ne.jp
|
58
|
+
Content-Type: message/rfc822
|
59
|
+
|
60
|
+
Return-Path: <nekochan@email.example.jp>
|
61
|
+
Received: from mx311.ume.example.ne.jp (localhost [127.0.0.1])
|
62
|
+
by mx311.ume.example.ne.jp (8.16.1/8.16.1) with ESMTP id ujmeV2ZG033926
|
63
|
+
for <kijitora-cat@google.example.com>; Wed, 7 Feb 2024 23:34:45 +0900 (JST)
|
64
|
+
(envelope-from nekochan@email.example.jp)
|
65
|
+
Received: (from kijitora@localhost)
|
66
|
+
by mx311.ume.example.ne.jp (8.16.1/8.16.1/Submit) id BJObadmZ060489
|
67
|
+
for kijitora-cat@google.example.com; Wed, 7 Feb 2024 23:34:45 +0900 (JST)
|
68
|
+
(envelope-from nekochan@email.example.jp)
|
69
|
+
Received: from relay1.email.example.jp (relay1.email.example.jp [192.168.168.168])
|
70
|
+
by mx311.ume.example.ne.jp (8.16.1/8.16.1) with ESMTPS id Mf73VCB9P8z4lM3b
|
71
|
+
(version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO)
|
72
|
+
for <neko@libsisimai.org>; Wed, 7 Feb 2024 23:34:45 +0900 (JST)
|
73
|
+
(envelope-from nekochan@email.example.jp)
|
74
|
+
Content-Type: text/plain; charset=us-ascii
|
75
|
+
Message-Id: <50ad190.20240207.2202@relay1.email.example.jp>
|
76
|
+
Content-Transfer-Encoding: 7bit
|
77
|
+
Subject: Nyaan
|
78
|
+
From: <nekochan@email.example.jp>
|
79
|
+
To: <neko@libsisimai.org>
|
80
|
+
Date: 7 Feb 2024 23:34:45 +0900
|
81
|
+
MIME-Version: 1.0
|
82
|
+
|
83
|
+
Nyaan?
|
84
|
+
|
85
|
+
--3LGub1et091679.1157497350/mx311.ume.example.ne.jp--
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sisimai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- azumakuniyuki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-03-
|
11
|
+
date: 2024-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -74,6 +74,7 @@ executables: []
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- ".github/workflows/rake-test.yml"
|
77
78
|
- ".gitignore"
|
78
79
|
- ".rubocop.yml"
|
79
80
|
- ".travis.yml"
|
@@ -640,6 +641,7 @@ files:
|
|
640
641
|
- set-of-emails/maildir/bsd/lhost-sendmail-57.eml
|
641
642
|
- set-of-emails/maildir/bsd/lhost-sendmail-58.eml
|
642
643
|
- set-of-emails/maildir/bsd/lhost-sendmail-59.eml
|
644
|
+
- set-of-emails/maildir/bsd/lhost-sendmail-60.eml
|
643
645
|
- set-of-emails/maildir/bsd/lhost-surfcontrol-01.eml
|
644
646
|
- set-of-emails/maildir/bsd/lhost-surfcontrol-02.eml
|
645
647
|
- set-of-emails/maildir/bsd/lhost-surfcontrol-03.eml
|