sisimai 5.3.0-java → 5.4.0-java
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/ChangeLog.md +12 -0
- data/Makefile +3 -2
- data/README-JA.md +2 -2
- data/README.md +6 -6
- data/lib/sisimai/address.rb +45 -56
- data/lib/sisimai/arf.rb +11 -16
- data/lib/sisimai/datetime.rb +16 -50
- data/lib/sisimai/fact/json.rb +5 -5
- data/lib/sisimai/fact/yaml.rb +3 -3
- data/lib/sisimai/fact.rb +11 -12
- data/lib/sisimai/lda.rb +3 -3
- data/lib/sisimai/lhost/activehunter.rb +4 -6
- data/lib/sisimai/lhost/amazonses.rb +5 -6
- data/lib/sisimai/lhost/apachejames.rb +7 -9
- data/lib/sisimai/lhost/biglobe.rb +3 -5
- data/lib/sisimai/lhost/courier.rb +4 -6
- data/lib/sisimai/lhost/domino.rb +4 -5
- data/lib/sisimai/lhost/dragonfly.rb +3 -5
- data/lib/sisimai/lhost/einsundeins.rb +6 -8
- data/lib/sisimai/lhost/exchange2003.rb +10 -12
- data/lib/sisimai/lhost/exchange2007.rb +4 -5
- data/lib/sisimai/lhost/exim.rb +6 -8
- data/lib/sisimai/lhost/ezweb.rb +10 -12
- data/lib/sisimai/lhost/fml.rb +2 -3
- data/lib/sisimai/lhost/gmail.rb +4 -6
- data/lib/sisimai/lhost/gmx.rb +6 -8
- data/lib/sisimai/lhost/googlegroups.rb +1 -2
- data/lib/sisimai/lhost/googleworkspace.rb +3 -4
- data/lib/sisimai/lhost/imailserver.rb +6 -7
- data/lib/sisimai/lhost/interscanmss.rb +1 -2
- data/lib/sisimai/lhost/kddi.rb +5 -8
- data/lib/sisimai/lhost/mailfoundry.rb +4 -7
- data/lib/sisimai/lhost/mailmarshalsmtp.rb +4 -6
- data/lib/sisimai/lhost/messagingserver.rb +5 -7
- data/lib/sisimai/lhost/mfilter.rb +4 -6
- data/lib/sisimai/lhost/notes.rb +7 -9
- data/lib/sisimai/lhost/opensmtpd.rb +2 -4
- data/lib/sisimai/lhost/postfix.rb +8 -11
- data/lib/sisimai/lhost/qmail.rb +5 -8
- data/lib/sisimai/lhost/sendmail.rb +7 -10
- data/lib/sisimai/lhost/v5sendmail.rb +15 -17
- data/lib/sisimai/lhost/verizon.rb +9 -14
- data/lib/sisimai/lhost/x1.rb +4 -6
- data/lib/sisimai/lhost/x2.rb +5 -7
- data/lib/sisimai/lhost/x3.rb +3 -4
- data/lib/sisimai/lhost/x6.rb +4 -6
- data/lib/sisimai/lhost/zoho.rb +6 -8
- data/lib/sisimai/lhost.rb +1 -1
- data/lib/sisimai/mail/mbox.rb +1 -1
- data/lib/sisimai/mail/memory.rb +1 -1
- data/lib/sisimai/mail.rb +8 -8
- data/lib/sisimai/message.rb +11 -13
- data/lib/sisimai/reason/authfailure.rb +10 -10
- data/lib/sisimai/reason/badreputation.rb +4 -6
- data/lib/sisimai/reason/blocked.rb +6 -8
- data/lib/sisimai/reason/contenterror.rb +5 -6
- data/lib/sisimai/reason/delivered.rb +2 -2
- data/lib/sisimai/reason/exceedlimit.rb +7 -8
- data/lib/sisimai/reason/expired.rb +6 -7
- data/lib/sisimai/reason/failedstarttls.rb +5 -7
- data/lib/sisimai/reason/feedback.rb +2 -2
- data/lib/sisimai/reason/filtered.rb +7 -10
- data/lib/sisimai/reason/hasmoved.rb +4 -5
- data/lib/sisimai/reason/hostunknown.rb +6 -7
- data/lib/sisimai/reason/mailboxfull.rb +7 -8
- data/lib/sisimai/reason/mailererror.rb +5 -8
- data/lib/sisimai/reason/mesgtoobig.rb +5 -6
- data/lib/sisimai/reason/networkerror.rb +5 -8
- data/lib/sisimai/reason/norelaying.rb +4 -5
- data/lib/sisimai/reason/notaccept.rb +5 -8
- data/lib/sisimai/reason/notcompliantrfc.rb +5 -6
- data/lib/sisimai/reason/onhold.rb +6 -9
- data/lib/sisimai/reason/policyviolation.rb +6 -9
- data/lib/sisimai/reason/rejected.rb +5 -6
- data/lib/sisimai/reason/requireptr.rb +6 -7
- data/lib/sisimai/reason/securityerror.rb +6 -9
- data/lib/sisimai/reason/spamdetected.rb +8 -9
- data/lib/sisimai/reason/speeding.rb +6 -7
- data/lib/sisimai/reason/suppressed.rb +3 -7
- data/lib/sisimai/reason/suspend.rb +5 -7
- data/lib/sisimai/reason/syntaxerror.rb +3 -5
- data/lib/sisimai/reason/systemerror.rb +6 -9
- data/lib/sisimai/reason/systemfull.rb +5 -8
- data/lib/sisimai/reason/toomanyconn.rb +5 -6
- data/lib/sisimai/reason/undefined.rb +2 -2
- data/lib/sisimai/reason/userunknown.rb +8 -9
- data/lib/sisimai/reason/vacation.rb +4 -5
- data/lib/sisimai/reason/virusdetected.rb +4 -5
- data/lib/sisimai/reason.rb +13 -13
- data/lib/sisimai/rfc1123.rb +4 -8
- data/lib/sisimai/rfc1894.rb +5 -6
- data/lib/sisimai/rfc2045.rb +27 -31
- data/lib/sisimai/rfc3464/thirdparty.rb +1 -1
- data/lib/sisimai/rfc3464.rb +7 -9
- data/lib/sisimai/rfc3834.rb +5 -9
- data/lib/sisimai/rfc5322.rb +8 -26
- data/lib/sisimai/rfc791.rb +6 -4
- data/lib/sisimai/rhost/google.rb +8 -0
- data/lib/sisimai/rhost/microsoft.rb +17 -5
- data/lib/sisimai/rhost.rb +2 -2
- data/lib/sisimai/smtp/command.rb +1 -1
- data/lib/sisimai/smtp/failure.rb +5 -12
- data/lib/sisimai/smtp/reply.rb +3 -5
- data/lib/sisimai/smtp/status.rb +14 -24
- data/lib/sisimai/smtp/transcript.rb +1 -10
- data/lib/sisimai/string.rb +20 -30
- data/lib/sisimai/version.rb +1 -1
- data/lib/sisimai.rb +11 -11
- data/set-of-emails/maildir/bsd/rhost-microsoft-06.eml +45 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e7c01c8568c5c1922dc71d9948d86eb8684bf18a981243b3e476a2de4d187f6
|
4
|
+
data.tar.gz: 0ca7b2415f6afc00b98f9e8e25b939f9962d0aba7c08f0c1689647a26f6329ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 201468479dbd8c7cf75d6ac3d7560e2ec9bf9badb6d9a847c5979264138477699450a15a9553e1e919f69dd8f369c9792af6fac56e2a07eea5940daa4a943273
|
7
|
+
data.tar.gz: 933f217ffb3bc6092e2f338a4df40291e11ad3c59b6bdb22e56ba53d9b88368ff21ab37687d2dc87873f3ce4327aede14913616312eff1ee716768a7c960b816
|
data/ChangeLog.md
CHANGED
@@ -3,6 +3,18 @@ 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.4.0
|
7
|
+
---------------------------------------------------------------------------------------------------
|
8
|
+
- release: "Tue, 1 Jul 2025 20:22:22 +0900 (JST)"
|
9
|
+
- version: "5.4.0"
|
10
|
+
- changes:
|
11
|
+
- #341 #342 #345 Implement the new status code `5.7.515` as `authfailure` and other undocumented
|
12
|
+
status codes begin with `4.4.` as `systemerror` of Microsoft in `Sisimai::Rhost::Microsoft`.
|
13
|
+
- #343 #346 Some internal methods do not return `nil`, standardize return values to align with
|
14
|
+
zero-value principle.
|
15
|
+
- #347 Support a frozen string literal
|
16
|
+
- #349 #350 Implement new status codes of Google: `4.7.40` and `5.7.32` as `authfailure`.
|
17
|
+
|
6
18
|
v5.3.0
|
7
19
|
---------------------------------------------------------------------------------------------------
|
8
20
|
- release: "Sat, 29 Mar 2025 06:04:41 +0900 (JST)"
|
data/Makefile
CHANGED
@@ -16,6 +16,7 @@ CP := cp
|
|
16
16
|
RM := rm -f
|
17
17
|
|
18
18
|
DEPENDENCIES = bundle rake minitest
|
19
|
+
RUBYARGUMENT = --enable-frozen-string-literal
|
19
20
|
.DEFAULT_GOAL = git-status
|
20
21
|
REPOS_TARGETS = git-status git-push git-commit-amend git-tag-list git-diff git-reset-soft \
|
21
22
|
git-rm-cached git-branch
|
@@ -58,11 +59,11 @@ release:
|
|
58
59
|
test: user-test author-test
|
59
60
|
user-test:
|
60
61
|
# Suppress warning messages until v5.5.0
|
61
|
-
rake publictest 2> /dev/null
|
62
|
+
RUBYOPT="$(RUBYARGUMENT)" rake publictest 2> /dev/null
|
62
63
|
|
63
64
|
author-test:
|
64
65
|
# Suppress warning messages until v5.5.0
|
65
|
-
rake privatetest 2> /dev/null
|
66
|
+
RUBYOPT="$(RUBYARGUMENT)" rake privatetest 2> /dev/null
|
66
67
|
|
67
68
|
check:
|
68
69
|
find lib -type f -exec grep --color -E ' $$' {} /dev/null \;
|
data/README-JA.md
CHANGED
@@ -23,7 +23,7 @@
|
|
23
23
|
> SisimaiはPerlモジュールまたはRuby Gemですが、PHPやPython、GoやRustなどJSONを読める言語であれば
|
24
24
|
> どのような環境においても解析結果を得ることでバウンスの発生状況を捉えるのにとても有用です。
|
25
25
|
|
26
|
-
- [**README(
|
26
|
+
- [**README(🇬🇧)**](README.md)
|
27
27
|
- [シシマイ? | What is Sisimai](#what-is-sisimai)
|
28
28
|
- [主な特徴的機能 | The key features](#the-key-features-of-sisimai)
|
29
29
|
- [コマンドラインでのデモ | Command line demo](#command-line-demo)
|
@@ -447,7 +447,7 @@ Related sites
|
|
447
447
|
|
448
448
|
See also
|
449
449
|
---------------------------------------------------------------------------------------------------
|
450
|
-
* [README.md - README.md in English](https://github.com/sisimai/rb-sisimai/blob/master/README.md)
|
450
|
+
* [README.md - README.md in English(🇬🇧)](https://github.com/sisimai/rb-sisimai/blob/master/README.md)
|
451
451
|
* [RFC3463 - Enhanced Mail System Status Codes](https://tools.ietf.org/html/rfc3463)
|
452
452
|
* [RFC3464 - An Extensible Message Format for Delivery Status Notifications](https://tools.ietf.org/html/rfc3464)
|
453
453
|
* [RFC3834 - Recommendations for Automatic Responses to Electronic Mail](https://tools.ietf.org/html/rfc3834)
|
data/README.md
CHANGED
@@ -25,7 +25,7 @@
|
|
25
25
|
> such as PHP, Python, Go, and Rust. By obtaining the analysis results, it is very useful for understanding
|
26
26
|
> the bounce occurrence status.
|
27
27
|
|
28
|
-
- [**README-JA(
|
28
|
+
- [**README-JA(🇯🇵)**](README-JA.md)
|
29
29
|
- [What is Sisimai](#what-is-sisimai)
|
30
30
|
- [The key features of sisimai](#the-key-features-of-sisimai)
|
31
31
|
- [Command line demo](#command-line-demo)
|
@@ -57,10 +57,10 @@
|
|
57
57
|
|
58
58
|
What is Sisimai
|
59
59
|
===================================================================================================
|
60
|
-
Sisimai is a library that decodes complex and diverse bounce emails and
|
61
|
-
delivery failure, such as the reason for the bounce and the recipient
|
62
|
-
data. It is also possible to output in JSON format. The Ruby version
|
63
|
-
Perl version of Sisimai
|
60
|
+
Sisimai (pronounced /ɕi.ɕi.ma.i/) is a library that decodes complex and diverse bounce emails and
|
61
|
+
outputs the results of the delivery failure, such as the reason for the bounce and the recipient
|
62
|
+
email address, in structured data. It is also possible to output in JSON format. The Ruby version
|
63
|
+
of Sisimai is ported from [the Perl version of Sisimai](https://github.com/sisimai/p5-sisimai/).
|
64
64
|
|
65
65
|

|
66
66
|
|
@@ -450,7 +450,7 @@ Related Sites
|
|
450
450
|
|
451
451
|
See also
|
452
452
|
---------------------------------------------------------------------------------------------------
|
453
|
-
* [README-JA.md - README.md in Japanese(
|
453
|
+
* [README-JA.md - README.md in Japanese(🇯🇵)](https://github.com/sisimai/rb-sisimai/blob/master/README-JA.md)
|
454
454
|
* [RFC3463 - Enhanced Mail System Status Codes](https://tools.ietf.org/html/rfc3463)
|
455
455
|
* [RFC3464 - An Extensible Message Format for Delivery Status Notifications](https://tools.ietf.org/html/rfc3464)
|
456
456
|
* [RFC3834 - Recommendations for Automatic Responses to Electronic Mail](https://tools.ietf.org/html/rfc3834)
|
data/lib/sisimai/address.rb
CHANGED
@@ -12,7 +12,7 @@ module Sisimai
|
|
12
12
|
# dtext = NO-WS-CTL / ; Non white space controls
|
13
13
|
# %d33-90 / ; The rest of the US-ASCII
|
14
14
|
# %d94-126 ; characters not including "[", "]", or "\"
|
15
|
-
re = {
|
15
|
+
re = {rfc5322: nil, ignored: nil, domain: nil}
|
16
16
|
atom = %r([a-zA-Z0-9_!#\$\%&'*+/=?\^`{}~|\-]+)o
|
17
17
|
quoted_string = %r/"(?:\\[^\r\n]|[^\\"])*"/o
|
18
18
|
domain_literal = %r/\[(?:\\[\x01-\x09\x0B-\x0c\x0e-\x7f]|[\x21-\x5a\x5e-\x7e])*\]/o
|
@@ -33,7 +33,7 @@ module Sisimai
|
|
33
33
|
:'quoted-string' => (1 << 1), # "Neko, Nyaan"
|
34
34
|
:'comment-block' => (1 << 2), # (neko)
|
35
35
|
}.freeze
|
36
|
-
Delimiters = {
|
36
|
+
Delimiters = {'<' => 1, '>' => 1, '(' => 1, ')' => 1, '"' => 1, ',' => 1}.freeze
|
37
37
|
|
38
38
|
# Return pseudo recipient or sender address
|
39
39
|
# @param [Symbol] argv0 Address type: true = recipient, false = sender
|
@@ -45,12 +45,10 @@ module Sisimai
|
|
45
45
|
|
46
46
|
# Check that the argument is an email address or not
|
47
47
|
# @param [String] email Email address string
|
48
|
-
# @return [
|
49
|
-
# false: is not an email address
|
48
|
+
# @return [Boolean] true: is an email address, false: is not an email address
|
50
49
|
def self.is_emailaddress(email)
|
51
|
-
return false
|
52
|
-
return false if email =~ %r/(?:[\x00-\x1f]|\x1f)/
|
53
|
-
return false if email.size > 254
|
50
|
+
return false if email.is_a?(::String) == false
|
51
|
+
return false if email =~ %r/(?:[\x00-\x1f]|\x1f)/ || email.size > 254
|
54
52
|
return true if email =~ Re[:ignored]
|
55
53
|
return false
|
56
54
|
end
|
@@ -60,9 +58,7 @@ module Sisimai
|
|
60
58
|
# @return [True,False] true: mailer-daemon
|
61
59
|
# false: Not mailer-daemon
|
62
60
|
def self.is_mailerdaemon(argv0 = nil)
|
63
|
-
return false
|
64
|
-
return false unless argv0.size > 0
|
65
|
-
return false unless argv0.is_a?(::String)
|
61
|
+
return false if argv0.to_s == "" || argv0.is_a?(::String) == false
|
66
62
|
|
67
63
|
email = argv0.downcase
|
68
64
|
postmaster = [
|
@@ -71,8 +67,7 @@ module Sisimai
|
|
71
67
|
].freeze
|
72
68
|
|
73
69
|
return true if postmaster.any? { |a| email.include?(a) }
|
74
|
-
return true if email == 'mailer-daemon'
|
75
|
-
return true if email == 'postmaster'
|
70
|
+
return true if email == 'mailer-daemon' || email == 'postmaster'
|
76
71
|
return false
|
77
72
|
end
|
78
73
|
|
@@ -87,7 +82,7 @@ module Sisimai
|
|
87
82
|
# #=> [{ address: 'neko@example.org', name: 'Neko', comment: '(nyaan)'}]
|
88
83
|
return nil unless argv1
|
89
84
|
|
90
|
-
emailtable = {
|
85
|
+
emailtable = {address: '', name: '', comment: ''}
|
91
86
|
addrtables = []
|
92
87
|
readbuffer = []
|
93
88
|
readcursor = 0
|
@@ -109,20 +104,20 @@ module Sisimai
|
|
109
104
|
# An email address has already been picked
|
110
105
|
if readcursor & Indicators[:'comment-block'] > 0
|
111
106
|
# The cursor is in the comment block (Neko, Nyaan)
|
112
|
-
v[:comment]
|
107
|
+
v[:comment] += e
|
113
108
|
elsif readcursor & Indicators[:'quoted-string'] > 0
|
114
109
|
# "Neko, Nyaan"
|
115
|
-
v[:name]
|
110
|
+
v[:name] += e
|
116
111
|
else
|
117
112
|
# The cursor is not in neither the quoted-string nor the comment block
|
118
113
|
readcursor = 0 # reset cursor position
|
119
114
|
readbuffer << v
|
120
|
-
v = {
|
115
|
+
v = {address: '', name: '', comment: ''}
|
121
116
|
p = ''
|
122
117
|
end
|
123
118
|
else
|
124
119
|
# "Neko, Nyaan" <neko@nyaan.example.org> OR <"neko,nyaan"@example.org>
|
125
|
-
p.empty? ? (v[:name]
|
120
|
+
p.empty? ? (v[:name] += e) : (v[p] += e)
|
126
121
|
end
|
127
122
|
next
|
128
123
|
end # End of if(',')
|
@@ -130,11 +125,11 @@ module Sisimai
|
|
130
125
|
if e == '<'
|
131
126
|
# <: The beginning of an email address or not
|
132
127
|
if v[:address].size > 0
|
133
|
-
p.empty? ? (v[:name]
|
128
|
+
p.empty? ? (v[:name] += e) : (v[p] += e)
|
134
129
|
else
|
135
130
|
# <neko@nyaan.example.org>
|
136
131
|
readcursor |= Indicators[:'email-address']
|
137
|
-
v[:address]
|
132
|
+
v[:address] += e
|
138
133
|
p = :address
|
139
134
|
end
|
140
135
|
next
|
@@ -146,11 +141,11 @@ module Sisimai
|
|
146
141
|
if readcursor & Indicators[:'email-address'] > 0
|
147
142
|
# <neko@example.org>
|
148
143
|
readcursor &= ~Indicators[:'email-address']
|
149
|
-
v[:address]
|
144
|
+
v[:address] += e
|
150
145
|
p = ''
|
151
146
|
else
|
152
147
|
# a comment block or a display name
|
153
|
-
p.empty? ? (v[:name]
|
148
|
+
p.empty? ? (v[:name] == e) : (v[:comment] -= e)
|
154
149
|
end
|
155
150
|
next
|
156
151
|
end # End of if('>')
|
@@ -161,27 +156,27 @@ module Sisimai
|
|
161
156
|
# <"neko(nyaan)"@example.org> or <neko(nyaan)@example.org>
|
162
157
|
if v[:address].include?('"')
|
163
158
|
# Quoted local part: <"neko(nyaan)"@example.org>
|
164
|
-
v[:address]
|
159
|
+
v[:address] += e
|
165
160
|
else
|
166
161
|
# Comment: <neko(nyaan)@example.org>
|
167
162
|
readcursor |= Indicators[:'comment-block']
|
168
|
-
v[:comment]
|
169
|
-
v[:comment]
|
163
|
+
v[:comment] += ' ' if v[:comment].end_with?(')')
|
164
|
+
v[:comment] += e
|
170
165
|
p = :comment
|
171
166
|
end
|
172
167
|
elsif readcursor & Indicators[:'comment-block'] > 0
|
173
168
|
# Comment at the outside of an email address (...(...)
|
174
|
-
v[:comment]
|
175
|
-
v[:comment]
|
169
|
+
v[:comment] += ' ' if v[:comment].end_with?(')')
|
170
|
+
v[:comment] += e
|
176
171
|
|
177
172
|
elsif readcursor & Indicators[:'quoted-string'] > 0
|
178
173
|
# "Neko, Nyaan(cat)", Deal as a display name
|
179
|
-
v[:name]
|
174
|
+
v[:name] += e
|
180
175
|
else
|
181
176
|
# The beginning of a comment block
|
182
177
|
readcursor |= Indicators[:'comment-block']
|
183
|
-
v[:comment]
|
184
|
-
v[:comment]
|
178
|
+
v[:comment] += ' ' if v[:comment].end_with?(')')
|
179
|
+
v[:comment] += e
|
185
180
|
p = :comment
|
186
181
|
end
|
187
182
|
next
|
@@ -193,22 +188,22 @@ module Sisimai
|
|
193
188
|
# <"neko(nyaan)"@example.org> OR <neko(nyaan)@example.org>
|
194
189
|
if v[:address].include?('"')
|
195
190
|
# Quoted string in the local part: <"neko(nyaan)"@example.org>
|
196
|
-
v[:address]
|
191
|
+
v[:address] += e
|
197
192
|
else
|
198
193
|
# Comment: <neko(nyaan)@example.org>
|
199
194
|
readcursor &= ~Indicators[:'comment-block']
|
200
|
-
v[:comment]
|
195
|
+
v[:comment] += e
|
201
196
|
p = :address
|
202
197
|
end
|
203
198
|
elsif readcursor & Indicators[:'comment-block'] > 0
|
204
199
|
# Comment at the outside of an email address (...(...)
|
205
200
|
readcursor &= ~Indicators[:'comment-block']
|
206
|
-
v[:comment]
|
201
|
+
v[:comment] += e
|
207
202
|
p = ''
|
208
203
|
else
|
209
204
|
# Deal as a display name
|
210
205
|
readcursor &= ~Indicators[:'comment-block']
|
211
|
-
v[:name]
|
206
|
+
v[:name] += e
|
212
207
|
p = ''
|
213
208
|
end
|
214
209
|
next
|
@@ -218,10 +213,10 @@ module Sisimai
|
|
218
213
|
# The beginning or the end of a quoted-string
|
219
214
|
if p.size > 0
|
220
215
|
# email-address or comment-block
|
221
|
-
v[p]
|
216
|
+
v[p] += e
|
222
217
|
else
|
223
218
|
# Display name like "Neko, Nyaan"
|
224
|
-
v[:name]
|
219
|
+
v[:name] += e
|
225
220
|
next unless readcursor & Indicators[:'quoted-string'] > 0
|
226
221
|
next if v[:name].end_with?(%Q|\x5c"|) # "Neko, Nyaan \"...
|
227
222
|
readcursor &= ~Indicators[:'quoted-string']
|
@@ -231,7 +226,7 @@ module Sisimai
|
|
231
226
|
end # End of if('"')
|
232
227
|
else
|
233
228
|
# The character is not a delimiter
|
234
|
-
p.empty? ? (v[:name]
|
229
|
+
p.empty? ? (v[:name] += e) : (v[p] += e)
|
235
230
|
next
|
236
231
|
end
|
237
232
|
end
|
@@ -283,7 +278,7 @@ module Sisimai
|
|
283
278
|
e.delete(:comment)
|
284
279
|
else
|
285
280
|
# Remove double-quotations, trailing spaces.
|
286
|
-
[:name, :comment].each { |f| e[f].strip
|
281
|
+
[:name, :comment].each { |f| e[f] = e[f].strip }
|
287
282
|
e[:comment] = '' unless e[:comment] =~ /\A[(].+[)]/
|
288
283
|
e[:name].squeeze!(' ') unless e[:name] =~ /\A["].+["]\z/
|
289
284
|
e[:name].sub!(/\A["]/, '') unless e[:name] =~ /\A["].+["][@]/
|
@@ -301,8 +296,8 @@ module Sisimai
|
|
301
296
|
# @return [String] Email address without comment, brackets
|
302
297
|
# @example s3s4('<neko@example.cat>') #=> 'neko@example.cat'
|
303
298
|
def self.s3s4(input)
|
304
|
-
return
|
305
|
-
return
|
299
|
+
return "" if input.to_s == ""
|
300
|
+
return "" if input.is_a?(Object::String) == false
|
306
301
|
|
307
302
|
addrs = Sisimai::Address.find(input, true) || []
|
308
303
|
return input if addrs.empty?
|
@@ -315,8 +310,8 @@ module Sisimai
|
|
315
310
|
# @example Expand VERP address
|
316
311
|
# expand_verp('bounce+neko=example.org@example.org') #=> 'neko@example.org'
|
317
312
|
def self.expand_verp(email)
|
318
|
-
return
|
319
|
-
return
|
313
|
+
return "" unless email.is_a? Object::String
|
314
|
+
return "" unless cv = email.split('@', 2).first.match(/\A[-\w]+?[+](\w[-.\w]+\w)[=](\w[-.\w]+\w)\z/)
|
320
315
|
verp0 = cv[1] + '@' + cv[2]
|
321
316
|
return verp0 if Sisimai::Address.is_emailaddress(verp0)
|
322
317
|
end
|
@@ -327,10 +322,10 @@ module Sisimai
|
|
327
322
|
# @example Expand alias
|
328
323
|
# expand_alias('neko+straycat@example.org') #=> 'neko@example.org'
|
329
324
|
def self.expand_alias(email)
|
330
|
-
return
|
325
|
+
return "" unless Sisimai::Address.is_emailaddress(email)
|
331
326
|
|
332
327
|
local = email.split('@')
|
333
|
-
return
|
328
|
+
return "" unless cv = local[0].match(/\A([-\w]+?)[+].+\z/)
|
334
329
|
return cv[1] + '@' + local[1]
|
335
330
|
end
|
336
331
|
|
@@ -347,11 +342,9 @@ module Sisimai
|
|
347
342
|
# Constructor of Sisimai::Address
|
348
343
|
# @param [Hash] argvs Email address, name, and other elements
|
349
344
|
# @return [Sisimai::Address] Object or nil when the email address was not valid.
|
350
|
-
# @example new(
|
345
|
+
# @example new(address: 'neko@example.org', name: 'Neko', comment: '(nyaan)') # => Sisimai::Address object
|
351
346
|
def initialize(argvs)
|
352
|
-
return nil
|
353
|
-
return nil unless argvs[:address]
|
354
|
-
return nil if argvs[:address].empty?
|
347
|
+
return nil if argvs.is_a?(Hash) == false || argvs[:address].nil? || argvs[:address].empty?
|
355
348
|
|
356
349
|
heads = ['<']
|
357
350
|
tails = ['>', ',', '.', ';']
|
@@ -364,10 +357,10 @@ module Sisimai
|
|
364
357
|
email = Sisimai::Address.expand_verp(argvs[:address])
|
365
358
|
aname = nil
|
366
359
|
|
367
|
-
|
360
|
+
if email.empty?
|
368
361
|
# Is not VERP address, try to expand the address as an alias
|
369
|
-
email = Sisimai::Address.expand_alias(argvs[:address])
|
370
|
-
aname = true
|
362
|
+
email = Sisimai::Address.expand_alias(argvs[:address])
|
363
|
+
aname = true if email.empty? == false
|
371
364
|
end
|
372
365
|
|
373
366
|
if email.include?('@')
|
@@ -422,15 +415,11 @@ module Sisimai
|
|
422
415
|
|
423
416
|
# Returns the value of address as String
|
424
417
|
# @return [String] Email address
|
425
|
-
def to_json(*)
|
426
|
-
return self.address.to_s
|
427
|
-
end
|
418
|
+
def to_json(*); return self.address.to_s; end
|
428
419
|
|
429
420
|
# Returns the value of address as String
|
430
421
|
# @return [String] Email address
|
431
|
-
def to_s
|
432
|
-
return self.address.to_s
|
433
|
-
end
|
422
|
+
def to_s; return self.address.to_s; end
|
434
423
|
|
435
424
|
end
|
436
425
|
end
|
data/lib/sisimai/arf.rb
CHANGED
@@ -53,12 +53,9 @@ module Sisimai
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
return true if heads["x-apple-unsubscribe"] == "true"
|
60
|
-
break
|
61
|
-
end
|
56
|
+
# X-Apple-Unsubscribe: true
|
57
|
+
return false unless heads.has_key?("x-apple-unsubscribe")
|
58
|
+
return true if heads["x-apple-unsubscribe"] == "true"
|
62
59
|
return false
|
63
60
|
end
|
64
61
|
|
@@ -70,7 +67,7 @@ module Sisimai
|
|
70
67
|
def inquire(mhead, mbody)
|
71
68
|
return nil unless self.is_arf(mhead)
|
72
69
|
|
73
|
-
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
70
|
+
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = dscontents[-1]
|
74
71
|
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
75
72
|
bodyslices = emailparts[0].split("\n")
|
76
73
|
reportpart = false
|
@@ -80,7 +77,6 @@ module Sisimai
|
|
80
77
|
remotehost = "" # The value of "Source-IP" field
|
81
78
|
reportedby = "" # The value of "Reporting-MTA" field
|
82
79
|
anotherone = "" # Other fields(append to Diagnosis)
|
83
|
-
v = dscontents[-1]
|
84
80
|
|
85
81
|
# 3.1. Required Fields
|
86
82
|
#
|
@@ -115,13 +111,12 @@ module Sisimai
|
|
115
111
|
# this is an autogenerated email abuse complaint regarding your network.
|
116
112
|
next unless Sisimai::String.aligned(r, f)
|
117
113
|
readcursor |= Indicators[:deliverystatus]
|
118
|
-
v["diagnosis"]
|
114
|
+
v["diagnosis"] += " #{e}"
|
119
115
|
break
|
120
116
|
end
|
121
117
|
next
|
122
118
|
end
|
123
|
-
next
|
124
|
-
next if e.empty?
|
119
|
+
next if (readcursor & Indicators[:deliverystatus]) > 0 || e.empty?
|
125
120
|
if e == ReportFrom then reportpart = true; next; end
|
126
121
|
|
127
122
|
if reportpart
|
@@ -158,12 +153,12 @@ module Sisimai
|
|
158
153
|
#
|
159
154
|
# Authentication-Results: mail.example.com;
|
160
155
|
# spf=fail smtp.mail=somespammer@example.com
|
161
|
-
anotherone
|
156
|
+
anotherone += "#{e}, "
|
162
157
|
|
163
158
|
elsif e.start_with?("User-Agent: ")
|
164
159
|
# The header field MUST appear exactly once.
|
165
160
|
# User-Agent: SomeGenerator/1.0
|
166
|
-
anotherone
|
161
|
+
anotherone += "#{e}, "
|
167
162
|
|
168
163
|
elsif e.start_with?("Received-Date: ") || e.start_with?("Arrival-Date: ")
|
169
164
|
# Arrival-Date header is optional and MUST NOT appear more than once.
|
@@ -185,11 +180,11 @@ module Sisimai
|
|
185
180
|
elsif e.start_with?("Original-Mail-From: ")
|
186
181
|
# the header is optional and MUST NOT appear more than once.
|
187
182
|
# Original-Mail-From: <somespammer@example.net>
|
188
|
-
anotherone
|
183
|
+
anotherone += "#{e}, "
|
189
184
|
end
|
190
185
|
else
|
191
186
|
# Messages before "Content-Type: message/feedback-report" part
|
192
|
-
v["diagnosis"]
|
187
|
+
v["diagnosis"] += " #{e}"
|
193
188
|
end
|
194
189
|
|
195
190
|
while recipients == 0
|
@@ -220,7 +215,7 @@ module Sisimai
|
|
220
215
|
end
|
221
216
|
return nil if recipients == 0
|
222
217
|
|
223
|
-
anotherone = ":
|
218
|
+
anotherone = ": #{Sisimai::String.sweep(anotherone)}" if anotherone != ""
|
224
219
|
anotherone = anotherone.chop if anotherone[-1, 1] == ","
|
225
220
|
|
226
221
|
j = -1
|
data/lib/sisimai/datetime.rb
CHANGED
@@ -4,34 +4,7 @@ module Sisimai
|
|
4
4
|
require 'date'
|
5
5
|
|
6
6
|
class << self
|
7
|
-
|
8
|
-
BASE_Y = 365.2425 # 1 year = 365.2425 days
|
9
|
-
BASE_L = 29.53059 # 1 lunar month = 29.53059 days
|
10
|
-
|
11
|
-
CONST_P = 4 * Math.atan2(1, 1) # PI, 3.1415926535
|
12
|
-
CONST_E = Math.exp(1) # e, Napier's constant
|
13
|
-
TZ_OFFSET = 54000 # Max time zone offset, 54000 seconds
|
14
|
-
|
15
|
-
TimeUnit = {
|
16
|
-
'o' => (BASE_D * BASE_Y * 4), # Olympiad, 4 years
|
17
|
-
'y' => (BASE_D * BASE_Y), # Year, Gregorian Calendar
|
18
|
-
'q' => (BASE_D * BASE_Y / 4), # Quarter, year/4
|
19
|
-
'l' => (BASE_D * BASE_L), # Lunar month
|
20
|
-
'f' => (BASE_D * 14), # Fortnight, 2 weeks
|
21
|
-
'w' => (BASE_D * 7), # Week, 604800 seconds
|
22
|
-
'd' => BASE_D, # Day
|
23
|
-
'h' => 3600, # Hour
|
24
|
-
'b' => 86.4, # Beat, Swatch internet time: 1000b = 1d
|
25
|
-
'm' => 60, # Minute,
|
26
|
-
's' => 1, # Second
|
27
|
-
}.freeze
|
28
|
-
|
29
|
-
MathematicalConstant = {
|
30
|
-
'e' => CONST_E,
|
31
|
-
'p' => CONST_P,
|
32
|
-
'g' => CONST_E**CONST_P,
|
33
|
-
}.freeze
|
34
|
-
|
7
|
+
TZ_OFFSET = 54000 # Max time zone offset, 54000 seconds
|
35
8
|
MonthName = {
|
36
9
|
full: %w[January February March April May June July August September October November December],
|
37
10
|
abbr: %w[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec],
|
@@ -191,12 +164,9 @@ module Sisimai
|
|
191
164
|
# parse("2015-11-03T23:34:45 Tue") #=> Tue, 3 Nov 2015 23:34:45 +0900
|
192
165
|
# parse("Tue, Nov 3 2015 2:2:2") #=> Tue, 3 Nov 2015 02:02:02 +0900
|
193
166
|
def parse(argv1)
|
194
|
-
return
|
195
|
-
return nil if argv1.empty?
|
167
|
+
return "" if argv1.is_a?(::String) == false || argv1.empty?
|
196
168
|
|
197
|
-
datestring = argv1
|
198
|
-
datestring.sub!(/[,](\d+)/, ', \1') # Thu,13 -> Thu, 13
|
199
|
-
datestring.sub!(/(\d{1,2}),/, '\1') # Apr,29 -> Apr 29
|
169
|
+
datestring = argv1.sub(/[,](\d+)/, ', \1').sub(/(\d{1,2}),/, '\1')
|
200
170
|
timetokens = datestring.split(' ')
|
201
171
|
afternoon1 = 0 # (Integer) After noon flag
|
202
172
|
altervalue = {} # (Hash) To store alternative values
|
@@ -267,7 +237,7 @@ module Sisimai
|
|
267
237
|
|
268
238
|
elsif p =~ /\A[(]?[A-Z]{2,5}[)]?\z/
|
269
239
|
# Timezone abbreviation; JST, GMT, UTC, ...
|
270
|
-
v[:z] ||= abbr2tz(p)
|
240
|
+
v[:z] ||= abbr2tz(p); v[:z] = "+0000" if v[:z].empty?
|
271
241
|
else
|
272
242
|
# Other date format
|
273
243
|
if cr = p.match(%r|\A(\d{4})[-/](\d{1,2})[-/](\d{1,2})\z|)
|
@@ -333,13 +303,9 @@ module Sisimai
|
|
333
303
|
if v.value?(nil)
|
334
304
|
# Strange date format
|
335
305
|
warn sprintf(' ***warning: Strange date format [%s]', datestring)
|
336
|
-
return
|
337
|
-
end
|
338
|
-
|
339
|
-
if v[:Y].to_i < 1902 || v[:Y].to_i > 2037
|
340
|
-
# -(2^31) ~ (2^31)
|
341
|
-
return nil
|
306
|
+
return ""
|
342
307
|
end
|
308
|
+
return "" if v[:Y].to_i < 1902 || v[:Y].to_i > 2037 # -(2^31) ~ (2^31)
|
343
309
|
|
344
310
|
# Build date string
|
345
311
|
# Thu, 29 Apr 2004 10:01:11 +0900
|
@@ -348,23 +314,23 @@ module Sisimai
|
|
348
314
|
|
349
315
|
# Abbreviation -> Tiemzone
|
350
316
|
# @param [String] argv1 Abbr. e.g.) JST, GMT, PDT
|
351
|
-
# @return [String
|
352
|
-
# not supported abbreviation
|
317
|
+
# @return [String] +0900, +0000, -0600 or an empty string if the argument is invalid
|
318
|
+
# format or not supported abbreviation
|
353
319
|
# @example Get the timezone string of "JST"
|
354
320
|
# abbr2tz('JST') #=> '+0900'
|
355
321
|
def abbr2tz(argv1)
|
356
|
-
return
|
357
|
-
return TimeZones[argv1]
|
322
|
+
return "" if argv1.is_a?(::String) == false
|
323
|
+
return TimeZones[argv1] || ""
|
358
324
|
end
|
359
325
|
|
360
326
|
# Convert to second
|
361
327
|
# @param [String] argv1 Timezone string e.g) +0900
|
362
|
-
# @return [Integer
|
328
|
+
# @return [Integer] n: seconds or 0 when the argument is invalid format
|
363
329
|
# @see second2tz
|
364
330
|
# @example Convert '+0900' to seconds
|
365
331
|
# tz2second('+0900') #=> 32400
|
366
332
|
def tz2second(argv1)
|
367
|
-
return
|
333
|
+
return 0 if argv1.is_a?(::String) == false
|
368
334
|
ztime = 0
|
369
335
|
|
370
336
|
if cr = argv1.match(/\A([-+])(\d)(\d)(\d{2})\z/)
|
@@ -378,13 +344,13 @@ module Sisimai
|
|
378
344
|
ztime += (digit[:'minutes'] * 60)
|
379
345
|
ztime *= -1 if digit[:'operator'] == '-'
|
380
346
|
|
381
|
-
return
|
347
|
+
return 0 if ztime.abs > TZ_OFFSET
|
382
348
|
return ztime
|
383
349
|
|
384
350
|
elsif argv1 =~ /\A[A-Za-z]+\z/
|
385
351
|
return tz2second(TimeZones[argv1])
|
386
352
|
else
|
387
|
-
return
|
353
|
+
return 0
|
388
354
|
end
|
389
355
|
end
|
390
356
|
|
@@ -396,9 +362,9 @@ module Sisimai
|
|
396
362
|
# second2tz(12345) #=> '+0325'
|
397
363
|
def second2tz(argv1)
|
398
364
|
return '+0000' unless argv1.is_a?(::Integer)
|
399
|
-
return
|
365
|
+
return "" if argv1.abs > TZ_OFFSET # UTC+14 + 1(DST?)
|
400
366
|
|
401
|
-
digit = {
|
367
|
+
digit = {:operator => '+'}
|
402
368
|
digit[:operator] = '-' if argv1 < 0
|
403
369
|
digit[:hours] = (argv1.abs / 3600).to_i
|
404
370
|
digit[:minutes] = ((argv1.abs % 3600) / 60).to_i
|
data/lib/sisimai/fact/json.rb
CHANGED
@@ -6,10 +6,10 @@ module Sisimai
|
|
6
6
|
class << self
|
7
7
|
# Serializer (JSON)
|
8
8
|
# @param [Sisimai::Fact] argvs Object
|
9
|
-
# @return [String
|
9
|
+
# @return [String] Dumped data or an empty string if the argument is missing
|
10
10
|
def dump(argvs)
|
11
|
-
return
|
12
|
-
return
|
11
|
+
return "" unless argvs
|
12
|
+
return "" unless argvs.is_a? Sisimai::Fact
|
13
13
|
|
14
14
|
if RUBY_PLATFORM.start_with?('java')
|
15
15
|
# java-based ruby environment like JRuby.
|
@@ -17,7 +17,7 @@ module Sisimai
|
|
17
17
|
require 'jrjackson'
|
18
18
|
jsonstring = JrJackson::Json.dump(argvs.damn)
|
19
19
|
rescue StandardError => ce
|
20
|
-
warn '***warning: Failed to JrJackson::Json.dump: '
|
20
|
+
warn '***warning: Failed to JrJackson::Json.dump: ' + ce.to_s
|
21
21
|
end
|
22
22
|
else
|
23
23
|
# MRI
|
@@ -25,7 +25,7 @@ module Sisimai
|
|
25
25
|
require 'oj'
|
26
26
|
jsonstring = Oj.dump(argvs.damn, :mode => :compat)
|
27
27
|
rescue StandardError => ce
|
28
|
-
warn '***warning: Failed to Oj.dump: '
|
28
|
+
warn '***warning: Failed to Oj.dump: ' + ce.to_s
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|