sisimai 4.25.6 → 4.25.11
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sisimai might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +4 -3
- data/ANALYTICAL-PRECISION +1 -1
- data/Benchmarks.mk +1 -1
- data/ChangeLog.md +71 -2
- data/Developers.mk +2 -2
- data/Makefile +1 -1
- data/README-JA.md +16 -16
- data/README.md +16 -16
- data/Repository.mk +1 -1
- data/lib/sisimai.rb +3 -3
- data/lib/sisimai/address.rb +6 -3
- data/lib/sisimai/arf.rb +25 -21
- data/lib/sisimai/data.rb +8 -29
- data/lib/sisimai/datetime.rb +18 -17
- data/lib/sisimai/lhost/amazonses.rb +1 -1
- data/lib/sisimai/lhost/domino.rb +29 -4
- data/lib/sisimai/lhost/einsundeins.rb +1 -1
- data/lib/sisimai/lhost/exchange2007.rb +1 -1
- data/lib/sisimai/lhost/exim.rb +12 -8
- data/lib/sisimai/lhost/ezweb.rb +1 -1
- data/lib/sisimai/lhost/fml.rb +0 -2
- data/lib/sisimai/lhost/gsuite.rb +5 -0
- data/lib/sisimai/lhost/imailserver.rb +1 -1
- data/lib/sisimai/lhost/mailfoundry.rb +3 -4
- data/lib/sisimai/lhost/mailmarshalsmtp.rb +2 -2
- data/lib/sisimai/lhost/mxlogic.rb +1 -1
- data/lib/sisimai/lhost/notes.rb +1 -1
- data/lib/sisimai/lhost/office365.rb +1 -1
- data/lib/sisimai/lhost/postfix.rb +3 -10
- data/lib/sisimai/lhost/qmail.rb +7 -7
- data/lib/sisimai/lhost/sendgrid.rb +2 -2
- data/lib/sisimai/lhost/sendmail.rb +1 -1
- data/lib/sisimai/lhost/verizon.rb +4 -4
- data/lib/sisimai/lhost/x3.rb +4 -0
- data/lib/sisimai/lhost/x4.rb +8 -8
- data/lib/sisimai/lhost/x5.rb +7 -3
- data/lib/sisimai/mail.rb +2 -21
- data/lib/sisimai/mda.rb +2 -2
- data/lib/sisimai/message.rb +47 -83
- data/lib/sisimai/reason/blocked.rb +40 -26
- data/lib/sisimai/reason/exceedlimit.rb +4 -1
- data/lib/sisimai/reason/expired.rb +2 -0
- data/lib/sisimai/reason/filtered.rb +1 -0
- data/lib/sisimai/reason/hostunknown.rb +3 -0
- data/lib/sisimai/reason/mailererror.rb +1 -1
- data/lib/sisimai/reason/norelaying.rb +5 -0
- data/lib/sisimai/reason/notaccept.rb +2 -0
- data/lib/sisimai/reason/policyviolation.rb +4 -0
- data/lib/sisimai/reason/rejected.rb +5 -1
- data/lib/sisimai/reason/securityerror.rb +5 -6
- data/lib/sisimai/reason/spamdetected.rb +17 -15
- data/lib/sisimai/reason/suspend.rb +1 -0
- data/lib/sisimai/reason/systemerror.rb +2 -0
- data/lib/sisimai/reason/userunknown.rb +25 -21
- data/lib/sisimai/rfc1894.rb +24 -22
- data/lib/sisimai/rfc3464.rb +4 -5
- data/lib/sisimai/rfc3834.rb +1 -1
- data/lib/sisimai/rfc5322.rb +8 -4
- data/lib/sisimai/rhost.rb +6 -8
- data/lib/sisimai/rhost/cox.rb +112 -0
- data/lib/sisimai/rhost/exchangeonline.rb +8 -1
- data/lib/sisimai/rhost/googleapps.rb +4 -1
- data/lib/sisimai/rhost/spectrum.rb +73 -0
- data/lib/sisimai/smtp/error.rb +8 -6
- data/lib/sisimai/smtp/reply.rb +1 -1
- data/lib/sisimai/smtp/status.rb +1 -1
- data/lib/sisimai/time.rb +1 -2
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/README.md +5 -4
- data/set-of-emails/maildir/bsd/arf-25.eml +61 -0
- data/set-of-emails/maildir/bsd/lhost-aol-04.eml +23 -23
- data/set-of-emails/maildir/bsd/lhost-domino-02.eml +1 -2
- data/set-of-emails/maildir/bsd/lhost-exim-61.eml +40 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-38.eml +1 -1
- data/set-of-emails/maildir/bsd/lhost-postfix-66.eml +80 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-67.eml +80 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-68.eml +82 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-69.eml +80 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-70.eml +87 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-71.eml +81 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-72.eml +81 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-73.eml +79 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-74.eml +83 -0
- data/set-of-emails/maildir/bsd/lhost-sendmail-08.eml +1 -1
- data/set-of-emails/maildir/bsd/lhost-sendmail-11.eml +1 -1
- data/set-of-emails/maildir/bsd/lhost-sendmail-57.eml +59 -0
- data/set-of-emails/maildir/bsd/lhost-sendmail-58.eml +70 -0
- data/set-of-emails/maildir/bsd/lhost-sendmail-59.eml +68 -0
- data/set-of-emails/maildir/bsd/lhost-x3-06.eml +53 -0
- data/set-of-emails/maildir/bsd/rhost-cox-01.eml +192 -0
- data/set-of-emails/maildir/bsd/{rhost-exchange-online-01.eml → rhost-exchangeonline-01.eml} +0 -0
- data/set-of-emails/maildir/bsd/{rhost-exchange-online-02.eml → rhost-exchangeonline-02.eml} +0 -0
- data/set-of-emails/maildir/bsd/{rhost-exchange-online-03.eml → rhost-exchangeonline-03.eml} +0 -0
- data/set-of-emails/maildir/bsd/{rhost-google-apps-01.eml → rhost-googleapps-01.eml} +0 -0
- data/set-of-emails/maildir/bsd/{rhost-google-apps-02.eml → rhost-googleapps-02.eml} +0 -0
- data/set-of-emails/maildir/bsd/rhost-spectrum-01.eml +111 -0
- data/set-of-emails/maildir/dos/{rhost-exchange-online-01.eml → rhost-exchangeonline-01.eml} +0 -0
- data/set-of-emails/maildir/dos/{rhost-google-apps-01.eml → rhost-googleapps-01.eml} +0 -0
- data/set-of-emails/maildir/mac/{rhost-exchange-online-01.eml → rhost-exchangeonline-01.eml} +0 -0
- data/set-of-emails/maildir/mac/{rhost-google-apps-01.eml → rhost-googleapps-01.eml} +0 -0
- data/sisimai-java.gemspec +4 -4
- data/sisimai.gemspec +4 -4
- metadata +39 -20
@@ -110,8 +110,7 @@ module Sisimai::Lhost
|
|
110
110
|
# The mail system
|
111
111
|
#
|
112
112
|
# <userunknown@example.co.jp>: host mx.example.co.jp[192.0.2.153] said: 550
|
113
|
-
# 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
|
114
|
-
# command)
|
113
|
+
# 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO command)
|
115
114
|
if readslices[-2].start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
|
116
115
|
# Continued line of the value of Diagnostic-Code header
|
117
116
|
v['diagnosis'] << ' ' << cv[1]
|
@@ -122,18 +121,12 @@ module Sisimai::Lhost
|
|
122
121
|
emailsteak[1] << cv[1] << ': ' << cv[2] << "\n"
|
123
122
|
|
124
123
|
else
|
125
|
-
if cv = e.match(/[ \t][(]in reply to ([A-Z]{4}).*/)
|
124
|
+
if cv = e.match(/[ \t][(]in reply to (?:end of )?([A-Z]{4}).*/) ||
|
125
|
+
cv = e.match(/([A-Z]{4})[ \t]*.*command[)]\z/)
|
126
126
|
# 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
|
127
127
|
commandset << cv[1]
|
128
128
|
anotherset['diagnosis'] ||= ''
|
129
129
|
anotherset['diagnosis'] << ' ' << e
|
130
|
-
|
131
|
-
elsif cv = e.match(/([A-Z]{4})[ \t]*.*command[)]\z/)
|
132
|
-
# to MAIL command)
|
133
|
-
commandset << cv[1]
|
134
|
-
anotherset['diagnosis'] ||= ''
|
135
|
-
anotherset['diagnosis'] << ' ' << e
|
136
|
-
|
137
130
|
else
|
138
131
|
# Alternative error message and recipient
|
139
132
|
if cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>] [(]expanded from [<](.+)[>][)]:[ \t]*(.+)\z/)
|
data/lib/sisimai/lhost/qmail.rb
CHANGED
@@ -23,29 +23,29 @@ module Sisimai::Lhost
|
|
23
23
|
ReSMTP = {
|
24
24
|
# Error text regular expressions which defined in qmail-remote.c
|
25
25
|
# qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
|
26
|
-
'conn' => %r/(?:Error:)?Connected to
|
26
|
+
'conn' => %r/(?:Error:)?Connected to [^ ]+ but greeting failed[.]/,
|
27
27
|
# qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
|
28
|
-
'ehlo' => %r/(?:Error:)?Connected to
|
28
|
+
'ehlo' => %r/(?:Error:)?Connected to [^ ]+ but my name was rejected[.]/,
|
29
29
|
# qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
|
30
30
|
# reason = rejected
|
31
|
-
'mail' => %r/(?:Error:)?Connected to
|
31
|
+
'mail' => %r/(?:Error:)?Connected to [^ ]+ but sender was rejected[.]/,
|
32
32
|
# qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
|
33
33
|
# qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
|
34
34
|
# reason = userunknown
|
35
|
-
'rcpt' => %r/(?:Error:)
|
35
|
+
'rcpt' => %r/(?:Error:)?[^ ]+ does not like recipient[.]/,
|
36
36
|
# qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
|
37
37
|
# qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
|
38
38
|
# qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
|
39
39
|
# qmail-remote.c:272| if (code >= 400) quit("Z"," failed after I sent the message");
|
40
40
|
'data' => %r{(?:
|
41
|
-
(?:Error:)
|
42
|
-
|(?:Error:)
|
41
|
+
(?:Error:)?[^ ]+[ ]failed[ ]on[ ]DATA[ ]command[.]
|
42
|
+
|(?:Error:)?[^ ]+[ ]failed[ ]after[ ]I[ ]sent[ ]the[ ]message[.]
|
43
43
|
)
|
44
44
|
}x,
|
45
45
|
}.freeze
|
46
46
|
# qmail-remote.c:261| if (!flagbother) quit("DGiving up on ","");
|
47
47
|
ReHost = %r{(?:
|
48
|
-
Giving[ ]up[ ]on[ ](
|
48
|
+
Giving[ ]up[ ]on[ ]([^ ]+[0-9a-zA-Z])[.]?\z
|
49
49
|
|Connected[ ]to[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]
|
50
50
|
|remote[ ]host[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]said:
|
51
51
|
)
|
@@ -86,7 +86,7 @@ module Sisimai::Lhost
|
|
86
86
|
# Arrival-Date: 2012-12-31 23-59-59
|
87
87
|
next unless cv = e.match(/\AArrival-Date: (\d{4})[-](\d{2})[-](\d{2}) (\d{2})[-](\d{2})[-](\d{2})\z/)
|
88
88
|
o[1] << 'Thu, ' << cv[3] + ' '
|
89
|
-
o[1] << Sisimai::DateTime.monthname(
|
89
|
+
o[1] << Sisimai::DateTime.monthname(false)[cv[2].to_i - 1]
|
90
90
|
o[1] << ' ' << cv[1] + ' ' << [cv[4], cv[5], cv[6]].join(':')
|
91
91
|
o[1] << ' ' << Sisimai::DateTime.abbr2tz('CDT')
|
92
92
|
else
|
@@ -124,7 +124,7 @@ module Sisimai::Lhost
|
|
124
124
|
if e['status'] == '5.0.0' || e['status'] == '4.0.0'
|
125
125
|
# Get the value of D.S.N. from the error message or the value of
|
126
126
|
# Diagnostic-Code header.
|
127
|
-
e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
|
127
|
+
e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || e['status']
|
128
128
|
end
|
129
129
|
|
130
130
|
if e['action'] == 'expired'
|
@@ -184,7 +184,7 @@ module Sisimai::Lhost
|
|
184
184
|
|
185
185
|
if anotherset['status']
|
186
186
|
# Check alternative status code
|
187
|
-
if e['status'].empty? || e['status'] !~ /\A[45][.]\d[.]\d\z/
|
187
|
+
if e['status'].empty? || e['status'] !~ /\A[45][.]\d[.]\d{1,3}\z/
|
188
188
|
# Override alternative status code
|
189
189
|
e['status'] = anotherset['status']
|
190
190
|
end
|
@@ -44,8 +44,8 @@ module Sisimai::Lhost
|
|
44
44
|
# The attempted recipient address does not exist.
|
45
45
|
'userunknown' => ['550 - Requested action not taken: no such user here'],
|
46
46
|
}
|
47
|
-
boundary00 = Sisimai::MIME.boundary(mhead['content-type']) || ''
|
48
|
-
rebackbone = Regexp.new('^' << Regexp.escape(
|
47
|
+
boundary00 = Sisimai::MIME.boundary(mhead['content-type'], 1) || ''
|
48
|
+
rebackbone = Regexp.new('^' << Regexp.escape(boundary00)) unless boundary00.empty?
|
49
49
|
emailsteak = Sisimai::RFC5322.fillet(mbody, rebackbone)
|
50
50
|
bodyslices = emailsteak[0].split("\n")
|
51
51
|
|
@@ -93,8 +93,8 @@ module Sisimai::Lhost
|
|
93
93
|
# vzwpix.com
|
94
94
|
startingof = { message: ['Message could not be delivered to mobile'] }
|
95
95
|
messagesof = { 'userunknown' => ['No valid recipients for this MM'] }
|
96
|
-
boundary00 = Sisimai::MIME.boundary(mhead['content-type'])
|
97
|
-
rebackbone = Regexp.new('^' << Regexp.escape(
|
96
|
+
boundary00 = Sisimai::MIME.boundary(mhead['content-type'], 1)
|
97
|
+
rebackbone = Regexp.new('^' << Regexp.escape(boundary00)) unless boundary00.empty?
|
98
98
|
emailsteak = Sisimai::RFC5322.fillet(mbody, rebackbone)
|
99
99
|
bodyslices = emailsteak[0].split("\n")
|
100
100
|
|
data/lib/sisimai/lhost/x3.rb
CHANGED
@@ -73,6 +73,10 @@ module Sisimai::Lhost
|
|
73
73
|
elsif cv = e.match(/\ARouting: (.+)/)
|
74
74
|
# Routing: Could not find a gateway for kijitora@example.co.jp
|
75
75
|
v['diagnosis'] = cv[1]
|
76
|
+
|
77
|
+
elsif cv = e.match(/\ADiagnostic-Code: smtp; (.+)/)
|
78
|
+
# Diagnostic-Code: smtp; 552 5.2.2 Over quota
|
79
|
+
v['diagnosis'] = cv[1]
|
76
80
|
end
|
77
81
|
end
|
78
82
|
end
|
data/lib/sisimai/lhost/x4.rb
CHANGED
@@ -19,7 +19,7 @@ module Sisimai::Lhost
|
|
19
19
|
# Characters: K,Z,D in qmail-qmqpc.c, qmail-send.c, qmail-rspawn.c
|
20
20
|
# K = success, Z = temporary error, D = permanent error
|
21
21
|
message: %r{\A(?>
|
22
|
-
He/Her[ ]is[ ]not.+[ ]user
|
22
|
+
He/Her[ ]is[ ]not[ ].+[ ]user
|
23
23
|
|Hi[.][ ].+[ ]unable[ ]to[ ]deliver[ ]your[ ]message[ ]to[ ]
|
24
24
|
the[ ]following[ ]addresses
|
25
25
|
|Su[ ]mensaje[ ]no[ ]pudo[ ]ser[ ]entregado
|
@@ -41,29 +41,29 @@ module Sisimai::Lhost
|
|
41
41
|
ReSMTP = {
|
42
42
|
# Error text regular expressions which defined in qmail-remote.c
|
43
43
|
# qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
|
44
|
-
'conn' => %r/(?:Error:)?Connected to
|
44
|
+
'conn' => %r/(?:Error:)?Connected to [^ ]+ but greeting failed[.]/,
|
45
45
|
# qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
|
46
|
-
'ehlo' => %r/(?:Error:)?Connected to
|
46
|
+
'ehlo' => %r/(?:Error:)?Connected to [^ ]+ but my name was rejected[.]/,
|
47
47
|
# qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
|
48
48
|
# reason = rejected
|
49
|
-
'mail' => %r/(?:Error:)?Connected to
|
49
|
+
'mail' => %r/(?:Error:)?Connected to [^ ]+ but sender was rejected[.]/,
|
50
50
|
# qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
|
51
51
|
# qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
|
52
52
|
# reason = userunknown
|
53
|
-
'rcpt' => %r/(?:Error:)
|
53
|
+
'rcpt' => %r/(?:Error:)?[^ ]+ does not like recipient[.]/,
|
54
54
|
# qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
|
55
55
|
# qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
|
56
56
|
# qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
|
57
57
|
# qmail-remote.c:272| if (code >= 400) quit("Z"," failed after I sent the message");
|
58
58
|
'data' => %r{(?:
|
59
|
-
(?:Error:)
|
60
|
-
|(?:Error:)
|
59
|
+
(?:Error:)?[^ ]+[ ]failed[ ]on[ ]DATA[ ]command[.]
|
60
|
+
|(?:Error:)?[^ ]+[ ]failed[ ]after[ ]I[ ]sent[ ]the[ ]message[.]
|
61
61
|
)
|
62
62
|
}x,
|
63
63
|
}.freeze
|
64
64
|
# qmail-remote.c:261| if (!flagbother) quit("DGiving up on ","");
|
65
65
|
ReHost = %r{(?:
|
66
|
-
Giving[ ]up[ ]on[ ](
|
66
|
+
Giving[ ]up[ ]on[ ]([^ ]+[0-9a-zA-Z])[.]?\z
|
67
67
|
|Connected[ ]to[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]
|
68
68
|
|remote[ ]host[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]said:
|
69
69
|
)
|
data/lib/sisimai/lhost/x5.rb
CHANGED
@@ -39,13 +39,17 @@ module Sisimai::Lhost
|
|
39
39
|
require 'sisimai/rfc1894'
|
40
40
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
41
41
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
42
|
-
emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
|
43
|
-
bodyslices = emailsteak[0].split("\n")
|
44
42
|
readslices = ['']
|
45
43
|
readcursor = 0 # (Integer) Points the current cursor position
|
46
44
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
47
45
|
v = nil
|
48
46
|
|
47
|
+
# Pick the second message/rfc822 part because the format of email-x5-*.eml is nested structure
|
48
|
+
prefillets = mbody.split(ReBackbone, 2)
|
49
|
+
prefillets[1].sub!(/\A.+?\n\n/s, '')
|
50
|
+
emailsteak = Sisimai::RFC5322.fillet(prefillets[1], ReBackbone)
|
51
|
+
bodyslices = emailsteak[0].split("\n")
|
52
|
+
|
49
53
|
while e = bodyslices.shift do
|
50
54
|
# Read error messages and delivery status lines from the head of the email
|
51
55
|
# to the previous line of the beginning of the original message.
|
@@ -56,7 +60,7 @@ module Sisimai::Lhost
|
|
56
60
|
readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
|
57
61
|
next
|
58
62
|
end
|
59
|
-
next if (readcursor & Indicators[
|
63
|
+
next if (readcursor & Indicators[:deliverystatus]) == 0
|
60
64
|
next if e.empty?
|
61
65
|
|
62
66
|
v = dscontents[-1]
|
data/lib/sisimai/mail.rb
CHANGED
@@ -4,7 +4,6 @@ module Sisimai
|
|
4
4
|
# Sisimai::Mail::Memory classes.
|
5
5
|
class Mail
|
6
6
|
# Imported from p5-Sisimail/lib/Sisimai/Mail.pm
|
7
|
-
Until = 'v4.25.10'
|
8
7
|
|
9
8
|
# :path [String] path to mbox or Maildir/
|
10
9
|
# :kind [String] Data type: mailbox, maildir, or stdin
|
@@ -25,7 +24,7 @@ module Sisimai
|
|
25
24
|
# Sisimai::Mail.new('<STDIN>')
|
26
25
|
classname = self.class.to_s << '::STDIN'
|
27
26
|
parameter['kind'] = 'stdin'
|
28
|
-
parameter['path'] =
|
27
|
+
parameter['path'] = '<STDIN>'
|
29
28
|
else
|
30
29
|
# The argumenet is a mailbox or a Maildir/.
|
31
30
|
mediatype = argv1.include?("\n") ? 'memory' : File.ftype(argv1)
|
@@ -52,6 +51,7 @@ module Sisimai
|
|
52
51
|
# The argument neither a mailbox nor a Maildir/.
|
53
52
|
classname = self.class.to_s << '::STDIN'
|
54
53
|
parameter['kind'] = 'stdin'
|
54
|
+
parameter['path'] = '<STDIN>'
|
55
55
|
end
|
56
56
|
return nil unless classname
|
57
57
|
|
@@ -64,16 +64,6 @@ module Sisimai
|
|
64
64
|
@data = parameter['data']
|
65
65
|
end
|
66
66
|
|
67
|
-
def mail
|
68
|
-
warn " ***warning: Sisimai::Mail.mail will be removed at #{Until}. Use Sisimai::Mail.data instead"
|
69
|
-
return data
|
70
|
-
end
|
71
|
-
|
72
|
-
def type
|
73
|
-
warn " ***warning: Sisimai::Mail.type will be removed at #{Until}. Use Sisimai::Mail.kind instead"
|
74
|
-
return kind
|
75
|
-
end
|
76
|
-
|
77
67
|
# Alias method of Sisimai::Mail.data.read()
|
78
68
|
# @return [String] Contents of mbox/Maildir
|
79
69
|
def read
|
@@ -81,15 +71,6 @@ module Sisimai
|
|
81
71
|
return data.read
|
82
72
|
end
|
83
73
|
|
84
|
-
# Close the handle
|
85
|
-
# @return [True,False] true: Successfully closed the handle
|
86
|
-
# false: Mail handle is not defined
|
87
|
-
def close
|
88
|
-
warn " ***warning: Sisimai::Mail.close will be removed at #{Until}. The handle automatically closes at the EOF"
|
89
|
-
return false unless data.handle
|
90
|
-
data.handle = nil
|
91
|
-
return true
|
92
|
-
end
|
93
74
|
end
|
94
75
|
end
|
95
76
|
|
data/lib/sisimai/mda.rb
CHANGED
@@ -7,7 +7,7 @@ module Sisimai
|
|
7
7
|
# dovecot/src/deliver/deliver.c
|
8
8
|
# 11: #define DEFAULT_MAIL_REJECTION_HUMAN_REASON \
|
9
9
|
# 12: "Your message to <%t> was automatically rejected:%n%r"
|
10
|
-
'dovecot' => %r/\AYour message to
|
10
|
+
'dovecot' => %r/\AYour message to [^ ]+ was automatically rejected:\z/,
|
11
11
|
'mail.local' => %r/\Amail[.]local: /,
|
12
12
|
'procmail' => %r/\Aprocmail: /,
|
13
13
|
'maildrop' => %r/\Amaildrop: /,
|
@@ -16,7 +16,7 @@ module Sisimai
|
|
16
16
|
}.freeze
|
17
17
|
MarkingsOf = {
|
18
18
|
message: %r{\A(?>
|
19
|
-
Your[ ]message[ ]to[ ]
|
19
|
+
Your[ ]message[ ]to[ ][^ ]+[ ]was[ ]automatically[ ]rejected:\z
|
20
20
|
|(?:mail[.]local|procmail|maildrop|vdelivermail|vdeliver):[ ]
|
21
21
|
)
|
22
22
|
}x
|
data/lib/sisimai/message.rb
CHANGED
@@ -32,102 +32,71 @@ module Sisimai
|
|
32
32
|
# value of the arguments are missing
|
33
33
|
def initialize(data: '', **argvs)
|
34
34
|
return nil if data.empty?
|
35
|
+
param = {}
|
35
36
|
email = data.scrub('?').gsub("\r\n", "\n")
|
37
|
+
thing = { 'from' => '','header' => {}, 'rfc822' => '', ds => [], 'catch' => nil }
|
36
38
|
|
37
|
-
methodargv = { 'data' => email, 'hook' => argvs[:hook] || nil }
|
39
|
+
#methodargv = { 'data' => email, 'hook' => argvs[:hook] || nil }
|
40
|
+
# 1. Load specified MTA modules
|
38
41
|
[:load, :order].each do |e|
|
39
42
|
# Order of MTA modules
|
40
43
|
next unless argvs[e]
|
41
44
|
next unless argvs[e].is_a? Array
|
42
45
|
next if argvs[e].empty?
|
43
|
-
|
46
|
+
param[e.to_s] = argvs[e]
|
44
47
|
end
|
48
|
+
tobeloaded = Sisimai::Message.load(param)
|
45
49
|
|
46
|
-
|
47
|
-
return nil unless datasource
|
48
|
-
return nil unless datasource['ds']
|
49
|
-
|
50
|
-
@from = datasource['from']
|
51
|
-
@header = datasource['header']
|
52
|
-
@ds = datasource['ds']
|
53
|
-
@rfc822 = datasource['rfc822']
|
54
|
-
@catch = datasource['catch'] || nil
|
55
|
-
end
|
56
|
-
|
57
|
-
# Check whether the object has valid content or not
|
58
|
-
# @return [True,False] returns true if the object is void
|
59
|
-
def void
|
60
|
-
return true unless @ds
|
61
|
-
return false
|
62
|
-
end
|
63
|
-
|
64
|
-
# Make data structure from the email message(a body part and headers)
|
65
|
-
# @param [Hash] argvs Email data
|
66
|
-
# @options argvs [String] data Entire email message
|
67
|
-
# @options argvs [Array] load User defined MTA module list
|
68
|
-
# @options argvs [Array] order The order of MTA modules
|
69
|
-
# @options argvs [Code] hook Reference to callback method
|
70
|
-
# @return [Hash] Resolved data structure
|
71
|
-
def self.make(argvs)
|
72
|
-
email = argvs['data']
|
73
|
-
|
74
|
-
hookmethod = argvs['hook'] || nil
|
75
|
-
processing = {
|
76
|
-
'from' => '', # From_ line
|
77
|
-
'header' => {}, # Email header
|
78
|
-
'rfc822' => '', # Original message part
|
79
|
-
'ds' => [], # Parsed data, Delivery Status
|
80
|
-
'catch' => nil, # Data parsed by callback method
|
81
|
-
}
|
82
|
-
methodargv = {
|
83
|
-
'load' => argvs['load'] || [],
|
84
|
-
'order' => argvs['order'] || []
|
85
|
-
}
|
86
|
-
tobeloaded = Sisimai::Message.load(methodargv)
|
87
|
-
|
88
|
-
# 1. Split email data to headers and a body part.
|
50
|
+
# 2. Split email data to headers and a body part.
|
89
51
|
return nil unless aftersplit = Sisimai::Message.divideup(email)
|
90
52
|
|
91
|
-
#
|
92
|
-
|
93
|
-
|
53
|
+
# 3. Convert email headers from text to hash reference
|
54
|
+
thing['from'] = aftersplit[0]
|
55
|
+
thing['header'] = Sisimai::Message.makemap(aftersplit[1])
|
94
56
|
|
95
|
-
#
|
96
|
-
unless
|
57
|
+
# 4. Decode and rewrite the "Subject:" header
|
58
|
+
unless thing['header']['subject'].empty?
|
97
59
|
# Decode MIME-Encoded "Subject:" header
|
98
|
-
s =
|
60
|
+
s = thing['header']['subject']
|
99
61
|
q = Sisimai::MIME.is_mimeencoded(s) ? Sisimai::MIME.mimedecode(s.split(/[ ]/)) : s
|
100
62
|
|
101
63
|
# Remove "Fwd:" string from the Subject: header
|
102
64
|
if cv = q.downcase.match(/\A[ \t]*fwd?:[ ]*(.*)\z/)
|
103
65
|
# Delete quoted strings, quote symbols(>)
|
104
66
|
q = cv[1]
|
105
|
-
aftersplit[
|
67
|
+
aftersplit[2] = aftersplit[2].gsub(/^[>]+[ ]/, '').gsub(/^[>]$/, '')
|
106
68
|
end
|
107
|
-
|
69
|
+
thing['header']['subject'] = q
|
108
70
|
end
|
109
71
|
|
110
|
-
#
|
111
|
-
|
112
|
-
|
113
|
-
'
|
114
|
-
'
|
115
|
-
'body' => aftersplit['body'],
|
116
|
-
'tryonfirst' => tryonfirst,
|
72
|
+
# 5. Rewrite message body for detecting the bounce reason
|
73
|
+
param = {
|
74
|
+
'hook' => argvs[:hook] || nil,
|
75
|
+
'mail' => thing,
|
76
|
+
'body' => aftersplit[2],
|
117
77
|
'tobeloaded' => tobeloaded,
|
78
|
+
'tryonfirst' => Sisimai::Order.make(thing['header']['subject'])
|
118
79
|
}
|
119
|
-
return nil unless bouncedata = Sisimai::Message.parse(
|
80
|
+
return nil unless bouncedata = Sisimai::Message.parse(param)
|
120
81
|
return nil if bouncedata.empty?
|
121
82
|
|
122
|
-
#
|
123
|
-
%w|ds catch rfc822|.each { |e|
|
83
|
+
# 6. Rewrite headers of the original message in the body part
|
84
|
+
%w|ds catch rfc822|.each { |e| thing[e] = bouncedata[e] }
|
124
85
|
p = bouncedata['rfc822']
|
125
|
-
p = aftersplit[
|
126
|
-
|
127
|
-
|
128
|
-
|
86
|
+
p = aftersplit[2] if p.empty?
|
87
|
+
thing['rfc822'] = p.is_a?(::String) ? Sisimai::Message.makemap(p, true) : p
|
88
|
+
|
89
|
+
@from = thing['from']
|
90
|
+
@header = thing['header']
|
91
|
+
@ds = thing['ds']
|
92
|
+
@rfc822 = thing['rfc822']
|
93
|
+
@catch = thing['catch'] || nil
|
129
94
|
end
|
130
95
|
|
96
|
+
# Check whether the object has valid content or not
|
97
|
+
# @return [True,False] returns true if the object is void
|
98
|
+
def void; return @ds ? false : true; end
|
99
|
+
|
131
100
|
# Load MTA modules which specified at 'order' and 'load' in the argument
|
132
101
|
# @param [Hash] argvs Module information to be loaded
|
133
102
|
# @options argvs [Array] load User defined MTA module list
|
@@ -171,28 +140,28 @@ module Sisimai
|
|
171
140
|
|
172
141
|
# Divide email data up headers and a body part.
|
173
142
|
# @param [String] email Email data
|
174
|
-
# @return [
|
143
|
+
# @return [Array] Email data after split
|
175
144
|
def self.divideup(email)
|
176
145
|
return nil if email.empty?
|
177
146
|
|
178
|
-
block =
|
147
|
+
block = ['', '', ''] # 0:From, 1:Header, 2:Body
|
179
148
|
email.gsub!(/\r\n/, "\n") if email.include?("\r\n")
|
180
149
|
email.gsub!(/[ \t]+$/, '') if email =~ /[ \t]+$/
|
181
150
|
|
182
|
-
(block[
|
183
|
-
return nil unless block[
|
184
|
-
return nil unless block[
|
151
|
+
(block[1], block[2]) = email.split(/\n\n/, 2)
|
152
|
+
return nil unless block[1]
|
153
|
+
return nil unless block[2]
|
185
154
|
|
186
|
-
if block[
|
155
|
+
if block[1].start_with?('From ')
|
187
156
|
# From MAILER-DAEMON Tue Feb 11 00:00:00 2014
|
188
|
-
block[
|
157
|
+
block[0] = block[1].split(/\n/, 2)[0].delete("\r")
|
189
158
|
else
|
190
159
|
# Set pseudo UNIX From line
|
191
|
-
block[
|
160
|
+
block[0] = 'MAILER-DAEMON Tue Feb 11 00:00:00 2014'
|
192
161
|
end
|
193
162
|
|
194
|
-
block[
|
195
|
-
block[
|
163
|
+
block[1] << "\n" unless block[1].end_with?("\n")
|
164
|
+
block[2] << "\n"
|
196
165
|
return block
|
197
166
|
end
|
198
167
|
|
@@ -307,12 +276,7 @@ module Sisimai
|
|
307
276
|
if hookmethod.is_a? Proc
|
308
277
|
# Call the hook method
|
309
278
|
begin
|
310
|
-
p = {
|
311
|
-
'datasrc' => 'email',
|
312
|
-
'headers' => mailheader,
|
313
|
-
'message' => bodystring,
|
314
|
-
'bounces' => nil
|
315
|
-
}
|
279
|
+
p = { 'headers' => mailheader, 'message' => bodystring }
|
316
280
|
havecaught = hookmethod.call(p)
|
317
281
|
rescue StandardError => ce
|
318
282
|
warn ' ***warning: Something is wrong in hook method :' << ce.to_s
|