sisimai 5.0.1 → 5.0.2
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 +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
|