sisimai 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/rake-test.yml +1 -1
- data/ChangeLog.md +102 -0
- data/Makefile +4 -2
- data/README-JA.md +23 -16
- data/README.md +22 -15
- data/lib/sisimai/arf.rb +121 -210
- data/lib/sisimai/fact.rb +208 -158
- data/lib/sisimai/lda.rb +98 -0
- data/lib/sisimai/lhost/activehunter.rb +1 -1
- data/lib/sisimai/lhost/amazonses.rb +185 -301
- data/lib/sisimai/lhost/apachejames.rb +48 -51
- data/lib/sisimai/lhost/biglobe.rb +1 -2
- data/lib/sisimai/lhost/courier.rb +10 -8
- data/lib/sisimai/lhost/domino.rb +25 -25
- data/lib/sisimai/lhost/dragonfly.rb +3 -4
- data/lib/sisimai/lhost/einsundeins.rb +3 -4
- data/lib/sisimai/lhost/exchange2003.rb +6 -8
- data/lib/sisimai/lhost/exchange2007.rb +111 -101
- data/lib/sisimai/lhost/exim.rb +232 -242
- data/lib/sisimai/lhost/ezweb.rb +43 -51
- data/lib/sisimai/lhost/fml.rb +2 -3
- data/lib/sisimai/lhost/gmail.rb +32 -28
- data/lib/sisimai/lhost/gmx.rb +4 -16
- data/lib/sisimai/lhost/googlegroups.rb +9 -8
- data/lib/sisimai/lhost/googleworkspace.rb +94 -0
- data/lib/sisimai/lhost/imailserver.rb +7 -16
- data/lib/sisimai/lhost/interscanmss.rb +1 -1
- data/lib/sisimai/lhost/kddi.rb +3 -4
- data/lib/sisimai/lhost/mailfoundry.rb +2 -5
- data/lib/sisimai/lhost/mailmarshalsmtp.rb +1 -2
- data/lib/sisimai/lhost/messagingserver.rb +14 -13
- data/lib/sisimai/lhost/mfilter.rb +4 -3
- data/lib/sisimai/lhost/notes.rb +2 -4
- data/lib/sisimai/lhost/opensmtpd.rb +2 -2
- data/lib/sisimai/lhost/postfix.rb +25 -27
- data/lib/sisimai/lhost/qmail.rb +130 -106
- data/lib/sisimai/lhost/sendmail.rb +19 -18
- data/lib/sisimai/lhost/v5sendmail.rb +88 -60
- data/lib/sisimai/lhost/verizon.rb +2 -2
- data/lib/sisimai/lhost/x1.rb +1 -1
- data/lib/sisimai/lhost/x2.rb +1 -2
- data/lib/sisimai/lhost/x3.rb +2 -2
- data/lib/sisimai/lhost/x6.rb +1 -1
- data/lib/sisimai/lhost/zoho.rb +2 -2
- data/lib/sisimai/lhost.rb +18 -21
- data/lib/sisimai/message.rb +93 -146
- data/lib/sisimai/order.rb +21 -77
- data/lib/sisimai/reason/authfailure.rb +1 -4
- data/lib/sisimai/reason/badreputation.rb +2 -2
- data/lib/sisimai/reason/blocked.rb +7 -10
- data/lib/sisimai/reason/contenterror.rb +7 -1
- data/lib/sisimai/reason/exceedlimit.rb +1 -4
- data/lib/sisimai/reason/failedstarttls.rb +42 -0
- data/lib/sisimai/reason/filtered.rb +5 -4
- data/lib/sisimai/reason/hasmoved.rb +1 -2
- data/lib/sisimai/reason/hostunknown.rb +3 -3
- data/lib/sisimai/reason/mailboxfull.rb +2 -4
- data/lib/sisimai/reason/mailererror.rb +1 -2
- data/lib/sisimai/reason/mesgtoobig.rb +2 -4
- data/lib/sisimai/reason/norelaying.rb +2 -3
- data/lib/sisimai/reason/notaccept.rb +2 -3
- data/lib/sisimai/reason/notcompliantrfc.rb +10 -4
- data/lib/sisimai/reason/rejected.rb +1 -1
- data/lib/sisimai/reason/requireptr.rb +2 -2
- data/lib/sisimai/reason/securityerror.rb +1 -3
- data/lib/sisimai/reason/spamdetected.rb +6 -8
- data/lib/sisimai/reason/speeding.rb +1 -2
- data/lib/sisimai/reason/suppressed.rb +36 -0
- data/lib/sisimai/reason/suspend.rb +1 -3
- data/lib/sisimai/reason/systemerror.rb +5 -0
- data/lib/sisimai/reason/toomanyconn.rb +1 -2
- data/lib/sisimai/reason/userunknown.rb +1 -1
- data/lib/sisimai/reason/virusdetected.rb +5 -6
- data/lib/sisimai/reason.rb +77 -73
- data/lib/sisimai/rfc1123.rb +152 -0
- data/lib/sisimai/rfc1894.rb +102 -62
- data/lib/sisimai/rfc2045.rb +2 -1
- data/lib/sisimai/rfc3464/thirdparty.rb +102 -0
- data/lib/sisimai/rfc3464.rb +222 -343
- data/lib/sisimai/rfc3834.rb +1 -1
- data/lib/sisimai/rfc5322.rb +7 -17
- data/lib/sisimai/rfc791.rb +69 -0
- data/lib/sisimai/rhost/aol.rb +36 -0
- data/lib/sisimai/rhost/apple.rb +5 -2
- data/lib/sisimai/rhost/cox.rb +3 -2
- data/lib/sisimai/rhost/facebook.rb +100 -0
- data/lib/sisimai/rhost/franceptt.rb +3 -2
- data/lib/sisimai/rhost/godaddy.rb +3 -2
- data/lib/sisimai/rhost/google.rb +19 -17
- data/lib/sisimai/rhost/gsuite.rb +42 -0
- data/lib/sisimai/rhost/iua.rb +3 -3
- data/lib/sisimai/rhost/kddi.rb +3 -2
- data/lib/sisimai/rhost/messagelabs.rb +37 -0
- data/lib/sisimai/rhost/microsoft.rb +56 -49
- data/lib/sisimai/rhost/mimecast.rb +29 -27
- data/lib/sisimai/rhost/nttdocomo.rb +4 -3
- data/lib/sisimai/rhost/outlook.rb +36 -0
- data/lib/sisimai/rhost/spectrum.rb +3 -2
- data/lib/sisimai/rhost/tencent.rb +3 -2
- data/lib/sisimai/rhost/yahooinc.rb +4 -3
- data/lib/sisimai/rhost.rb +69 -39
- data/lib/sisimai/smtp/command.rb +31 -21
- data/lib/sisimai/smtp/failure.rb +103 -0
- data/lib/sisimai/smtp/reply.rb +29 -25
- data/lib/sisimai/smtp/status.rb +36 -19
- data/lib/sisimai/smtp/transcript.rb +15 -15
- data/lib/sisimai/string.rb +0 -46
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/maildir/bsd/lhost-postfix-30.eml +81 -81
- data/set-of-emails/maildir/bsd/{lhost-aol-03.eml → rhost-aol-03.eml} +1264 -1264
- data/set-of-emails/maildir/bsd/{lhost-aol-04.eml → rhost-aol-04.eml} +1260 -1260
- data/set-of-emails/maildir/bsd/{lhost-aol-05.eml → rhost-aol-05.eml} +105 -105
- data/set-of-emails/maildir/bsd/{lhost-aol-06.eml → rhost-aol-06.eml} +105 -105
- data/set-of-emails/maildir/bsd/rhost-gsuite-01.eml +189 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-02.eml +180 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-03.eml +251 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-04.eml +211 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-05.eml +226 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-06.eml +257 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-07.eml +289 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-08.eml +231 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-09.eml +231 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-10.eml +254 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-11.eml +228 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-12.eml +271 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-13.eml +261 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-14.eml +273 -0
- data/set-of-emails/maildir/bsd/rhost-gsuite-15.eml +229 -0
- data/set-of-emails/maildir/bsd/{lhost-messagelabs-01.eml → rhost-messagelabs-01.eml} +93 -93
- data/set-of-emails/maildir/bsd/rhost-outlook-01.eml +72 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-02.eml +72 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-03.eml +72 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-04.eml +79 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-06.eml +75 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-07.eml +70 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-08.eml +70 -0
- data/set-of-emails/maildir/bsd/rhost-outlook-09.eml +56 -0
- data/set-of-emails/maildir/tmp/arf-22.eml +49 -0
- data/set-of-emails/maildir/tmp/arf-23.eml +49 -0
- data/set-of-emails/maildir/tmp/arf-24.eml +50 -0
- data/set-of-emails/maildir/tmp/lhost-exim-07.eml +28 -0
- metadata +73 -56
- data/lib/sisimai/lhost/amavis.rb +0 -163
- data/lib/sisimai/lhost/amazonworkmail.rb +0 -127
- data/lib/sisimai/lhost/aol.rb +0 -125
- data/lib/sisimai/lhost/barracuda.rb +0 -92
- data/lib/sisimai/lhost/bigfoot.rb +0 -125
- data/lib/sisimai/lhost/facebook.rb +0 -188
- data/lib/sisimai/lhost/gsuite.rb +0 -194
- data/lib/sisimai/lhost/mailru.rb +0 -214
- data/lib/sisimai/lhost/mcafee.rb +0 -109
- data/lib/sisimai/lhost/messagelabs.rb +0 -120
- data/lib/sisimai/lhost/mxlogic.rb +0 -198
- data/lib/sisimai/lhost/office365.rb +0 -252
- data/lib/sisimai/lhost/outlook.rb +0 -129
- data/lib/sisimai/lhost/powermta.rb +0 -118
- data/lib/sisimai/lhost/receivingses.rb +0 -126
- data/lib/sisimai/lhost/sendgrid.rb +0 -150
- data/lib/sisimai/lhost/surfcontrol.rb +0 -105
- data/lib/sisimai/lhost/x4.rb +0 -269
- data/lib/sisimai/lhost/x5.rb +0 -112
- data/lib/sisimai/lhost/yahoo.rb +0 -102
- data/lib/sisimai/lhost/yandex.rb +0 -118
- data/lib/sisimai/mda.rb +0 -121
- data/lib/sisimai/smtp/error.rb +0 -119
- /data/set-of-emails/maildir/bsd/{lhost-googlegroups-15.eml → lhost-googleworkspace-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-x4-08.eml → lhost-x2-06.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-01.eml → rfc3464-51.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-03.eml → rfc3464-52.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-04.eml → rfc3464-53.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-05.eml → rfc3464-54.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-06.eml → rfc3464-55.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-07.eml → rfc3464-56.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-08.eml → rfc3464-57.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-09.eml → rfc3464-58.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-10.eml → rfc3464-59.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-11.eml → rfc3464-60.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-12.eml → rfc3464-61.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-13.eml → rfc3464-62.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-14.eml → rfc3464-63.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-15.eml → rfc3464-64.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-gsuite-02.eml → rfc3464-65.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-aol-01.eml → rhost-aol-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-aol-02.eml → rhost-aol-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-facebook-03.eml → rhost-facebook-03.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-facebook-04.eml → rhost-facebook-04.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-messagelabs-02.eml → rhost-messagelabs-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{lhost-messagelabs-03.eml → rhost-messagelabs-03.eml} +0 -0
- /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-37.eml +0 -0
- /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-38.eml +0 -0
- /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-39.eml +0 -0
@@ -60,20 +60,21 @@ module Sisimai::Lhost
|
|
60
60
|
# Remote system: dns;mx.example.jp (TCP|17.111.174.67|47323|192.0.2.225|25) (6jo.example.jp ESMTP SENDMAIL-VM)
|
61
61
|
v = dscontents[-1]
|
62
62
|
|
63
|
-
if
|
64
|
-
|
65
|
-
|
63
|
+
if Sisimai::String.aligned(e, [' Recipient address: ', '@', '.']) ||
|
64
|
+
Sisimai::String.aligned(e, [' Original address: ', '@', '.'])
|
65
|
+
# Recipient address: @smtp.example.net:kijitora@server
|
66
|
+
# Original address: kijitora@example.jp
|
67
|
+
cv = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
|
68
|
+
next unless Sisimai::Address.is_emailaddress(cv)
|
69
|
+
|
70
|
+
if v["recipient"] != "" && cv != v['recipient']
|
66
71
|
# There are multiple recipient addresses in the message body.
|
67
72
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
68
73
|
v = dscontents[-1]
|
69
74
|
end
|
70
|
-
v['recipient'] =
|
75
|
+
v['recipient'] = cv
|
71
76
|
recipients += 1
|
72
77
|
|
73
|
-
elsif e.start_with?(' Original address: ') && e.index('@') > 1
|
74
|
-
# Original address: kijitora@example.jp
|
75
|
-
v['recipient'] = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
|
76
|
-
|
77
78
|
elsif e.start_with?(' Date: ')
|
78
79
|
# Date: Fri, 21 Nov 2014 23:34:45 +0900
|
79
80
|
v['date'] = e[e.index(':') + 2, e.size]
|
@@ -120,18 +121,18 @@ module Sisimai::Lhost
|
|
120
121
|
# Status: 5.1.1 (Remote SMTP server has rejected address)
|
121
122
|
p1 = e.index(':')
|
122
123
|
p2 = e.index('(')
|
123
|
-
v['status']
|
124
|
-
v['diagnosis']
|
124
|
+
v['status'] = e[p1 + 2, p2 - p1 - 3]
|
125
|
+
v['diagnosis'] = e[p2 + 1, e[e.index(')') - p2 - 1]] if v["diagnosis"].empty?
|
125
126
|
|
126
127
|
elsif e.start_with?('Arrival-Date: ')
|
127
128
|
# Arrival-date: Thu, 29 Apr 2014 23:34:45 +0000 (GMT)
|
128
|
-
v['date']
|
129
|
+
v['date'] = e[e.index(':') + 2, e.size] if v["date"].empty?
|
129
130
|
|
130
131
|
elsif e.start_with?('Reporting-MTA: ')
|
131
132
|
# Reporting-MTA: dns;mr21p30im-asmtp004.me.com (tcp-daemon)
|
132
133
|
localhost = e[e.index(';') + 1, e.size]
|
133
|
-
v['lhost']
|
134
|
-
v['lhost']
|
134
|
+
v['lhost'] = localhost if v["lhost"].empty?
|
135
|
+
v['lhost'] = localhost unless v['lhost'].index('.') > 0
|
135
136
|
end
|
136
137
|
end
|
137
138
|
end
|
@@ -62,7 +62,7 @@ module Sisimai::Lhost
|
|
62
62
|
if e.include?('@') && e.include?(' ') == false
|
63
63
|
# 以下のメールアドレスへの送信に失敗しました。
|
64
64
|
# kijitora@example.jp
|
65
|
-
if v[
|
65
|
+
if v["recipient"] != ""
|
66
66
|
# There are multiple recipient addresses in the message body.
|
67
67
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
68
68
|
v = dscontents[-1]
|
@@ -75,6 +75,7 @@ module Sisimai::Lhost
|
|
75
75
|
# DATA
|
76
76
|
next if v['command']
|
77
77
|
v['command'] = e if markingset['command']
|
78
|
+
|
78
79
|
else
|
79
80
|
# Get error message and SMTP command
|
80
81
|
if e == StartingOf[:error][0]
|
@@ -87,7 +88,7 @@ module Sisimai::Lhost
|
|
87
88
|
else
|
88
89
|
# 550 5.1.1 unknown user <kijitora@example.jp>
|
89
90
|
next if e.start_with?('-')
|
90
|
-
next if v['diagnosis']
|
91
|
+
next if v['diagnosis'] != ""
|
91
92
|
v['diagnosis'] = e
|
92
93
|
end
|
93
94
|
end
|
@@ -103,7 +104,7 @@ module Sisimai::Lhost
|
|
103
104
|
rheads = mhead['received']
|
104
105
|
rhosts = Sisimai::RFC5322.received(rheads[-1])
|
105
106
|
|
106
|
-
e['lhost']
|
107
|
+
e['lhost'] = Sisimai::RFC5322.received(rheads[0]).shift if e["lhost"].empty?
|
107
108
|
[rhosts[0], rhosts[1]].each do |ee|
|
108
109
|
# Avoid "... by m-FILTER"
|
109
110
|
next unless ee.include?('.')
|
data/lib/sisimai/lhost/notes.rb
CHANGED
@@ -58,12 +58,12 @@ module Sisimai::Lhost
|
|
58
58
|
v = dscontents[-1]
|
59
59
|
if e.include?('@') && e.index(' ').nil?
|
60
60
|
# kijitora@notes.example.jp
|
61
|
-
if v[
|
61
|
+
if v["recipient"] != ""
|
62
62
|
# There are multiple recipient addresses in the message body.
|
63
63
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
64
64
|
v = dscontents[-1]
|
65
65
|
end
|
66
|
-
v[
|
66
|
+
v["recipient"] = e if v["recipient"].empty?
|
67
67
|
recipients += 1
|
68
68
|
else
|
69
69
|
next if e.empty?
|
@@ -83,11 +83,9 @@ module Sisimai::Lhost
|
|
83
83
|
# No character set in Content-Type header
|
84
84
|
encodedmsg = removedmsg
|
85
85
|
end
|
86
|
-
v['diagnosis'] ||= ''
|
87
86
|
v['diagnosis'] << encodedmsg
|
88
87
|
else
|
89
88
|
# Error message does not include multi-byte character
|
90
|
-
v['diagnosis'] ||= ''
|
91
89
|
v['diagnosis'] << e
|
92
90
|
end
|
93
91
|
end
|
@@ -106,9 +106,9 @@ module Sisimai::Lhost
|
|
106
106
|
# Below is a copy of the original message:
|
107
107
|
v = dscontents[-1]
|
108
108
|
|
109
|
-
if
|
109
|
+
if Sisimai::String.aligned(e, ['@', ' '])
|
110
110
|
# kijitora@example.jp: 550 5.2.2 <kijitora@example>... Mailbox Full
|
111
|
-
if v[
|
111
|
+
if v["recipient"] != ""
|
112
112
|
# There are multiple recipient addresses in the message body.
|
113
113
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
114
114
|
v = dscontents[-1]
|
@@ -4,6 +4,8 @@ module Sisimai::Lhost
|
|
4
4
|
module Postfix
|
5
5
|
class << self
|
6
6
|
require 'sisimai/lhost'
|
7
|
+
require 'sisimai/rfc1123'
|
8
|
+
require 'sisimai/smtp/reply'
|
7
9
|
require 'sisimai/smtp/command'
|
8
10
|
|
9
11
|
# Postfix manual - bounce(5) - http://www.postfix.org/bounce.5.html
|
@@ -26,19 +28,17 @@ module Sisimai::Lhost
|
|
26
28
|
# @return [Hash] Bounce data list and message/rfc822 part
|
27
29
|
# @return [Nil] it failed to decode or the arguments are missing
|
28
30
|
def inquire(mhead, mbody)
|
29
|
-
match =
|
30
|
-
sessx = nil
|
31
|
+
match = 0
|
31
32
|
|
32
33
|
if mhead['subject'].include?('SMTP server: errors from ')
|
33
34
|
# src/smtpd/smtpd_chat.c:|337: post_mail_fprintf(notice, "Subject: %s SMTP server: errors from %s",
|
34
35
|
# src/smtpd/smtpd_chat.c:|338: var_mail_name, state->namaddr);
|
35
|
-
match =
|
36
|
-
sessx = true
|
36
|
+
match = 2
|
37
37
|
else
|
38
38
|
# Subject: Undelivered Mail Returned to Sender
|
39
|
-
match =
|
39
|
+
match = 1 if mhead['subject'] == 'Undelivered Mail Returned to Sender'
|
40
40
|
end
|
41
|
-
return nil
|
41
|
+
return nil if match == 0
|
42
42
|
return nil if mhead['x-aol-ip']
|
43
43
|
|
44
44
|
permessage = {} # (Hash) Store values of each Per-Message field
|
@@ -52,7 +52,7 @@ module Sisimai::Lhost
|
|
52
52
|
anotherset = {} # Another error information
|
53
53
|
v = nil
|
54
54
|
|
55
|
-
if
|
55
|
+
if match == 2
|
56
56
|
# The message body starts with 'Transcript of session follows.'
|
57
57
|
require 'sisimai/smtp/transcript'
|
58
58
|
transcript = Sisimai::SMTP::Transcript.rise(emailparts[0], 'In:', 'Out:')
|
@@ -75,7 +75,7 @@ module Sisimai::Lhost
|
|
75
75
|
|
76
76
|
elsif e['command'] == 'RCPT'
|
77
77
|
# RCPT TO: <...>
|
78
|
-
if v[
|
78
|
+
if v["recipient"] != ""
|
79
79
|
# There are multiple recipient addresses in the transcript of session
|
80
80
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
81
81
|
v = dscontents[-1]
|
@@ -86,9 +86,9 @@ module Sisimai::Lhost
|
|
86
86
|
|
87
87
|
next if p['reply'].to_i < 400
|
88
88
|
commandset << e['command']
|
89
|
-
v['diagnosis']
|
90
|
-
v['replycode']
|
91
|
-
v['status']
|
89
|
+
v['diagnosis'] = p['text'].join(' ') if v["diagnosis"].empty?
|
90
|
+
v['replycode'] = p['reply'] if v["replycode"].empty?
|
91
|
+
v['status'] = p['status'] if v["status"].empty?
|
92
92
|
end
|
93
93
|
else
|
94
94
|
fieldtable = Sisimai::RFC1894.FIELDTABLE
|
@@ -107,17 +107,18 @@ module Sisimai::Lhost
|
|
107
107
|
next if (readcursor & Indicators[:deliverystatus]) == 0
|
108
108
|
next if e.empty?
|
109
109
|
|
110
|
-
|
110
|
+
f = Sisimai::RFC1894.match(e)
|
111
|
+
if f > 0
|
111
112
|
# "e" matched with any field defined in RFC3464
|
112
113
|
next unless o = Sisimai::RFC1894.field(e)
|
113
114
|
v = dscontents[-1]
|
114
115
|
|
115
|
-
if o[
|
116
|
+
if o[3] == 'addr'
|
116
117
|
# Final-Recipient: rfc822; kijitora@example.jp
|
117
118
|
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
118
119
|
if o[0] == 'final-recipient'
|
119
120
|
# Final-Recipient: rfc822; kijitora@example.jp
|
120
|
-
if v[
|
121
|
+
if v["recipient"] != ""
|
121
122
|
# There are multiple recipient addresses in the message body.
|
122
123
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
123
124
|
v = dscontents[-1]
|
@@ -128,17 +129,18 @@ module Sisimai::Lhost
|
|
128
129
|
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
129
130
|
v['alias'] = o[2]
|
130
131
|
end
|
131
|
-
elsif o[
|
132
|
+
elsif o[3] == 'code'
|
132
133
|
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
133
134
|
v['spec'] = o[1]
|
134
|
-
v['spec'] = 'SMTP' if v['spec'] == 'X-POSTFIX'
|
135
|
+
v['spec'] = 'SMTP' if v['spec'].upcase == 'X-POSTFIX'
|
135
136
|
v['diagnosis'] = o[2]
|
136
137
|
else
|
137
138
|
# Other DSN fields defined in RFC3464
|
138
139
|
next unless fieldtable[o[0]]
|
140
|
+
next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false
|
139
141
|
v[fieldtable[o[0]]] = o[2]
|
140
142
|
|
141
|
-
next unless f
|
143
|
+
next unless f == 1
|
142
144
|
permessage[fieldtable[o[0]]] = o[2]
|
143
145
|
end
|
144
146
|
else
|
@@ -162,7 +164,7 @@ module Sisimai::Lhost
|
|
162
164
|
# Alternative error message and recipient
|
163
165
|
if e.include?(' (in reply to ') || e.include?('command)')
|
164
166
|
# 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
|
165
|
-
|
167
|
+
cv = Sisimai::SMTP::Command.find(e) || ""; commandset << cv unless cv.empty?
|
166
168
|
anotherset['diagnosis'] ||= ''
|
167
169
|
anotherset['diagnosis'] << ' ' << e
|
168
170
|
|
@@ -205,7 +207,7 @@ module Sisimai::Lhost
|
|
205
207
|
|
206
208
|
end
|
207
209
|
|
208
|
-
|
210
|
+
if recipients == 0
|
209
211
|
# Fallback: get a recipient address from error messages
|
210
212
|
if anotherset['recipient'].to_s.size > 0
|
211
213
|
# Set a recipient address
|
@@ -241,13 +243,9 @@ module Sisimai::Lhost
|
|
241
243
|
# More detailed error message is in "anotherset"
|
242
244
|
as = '' # status
|
243
245
|
ar = '' # replycode
|
244
|
-
|
245
|
-
e['status'] ||= ''
|
246
|
-
e['replycode'] ||= ''
|
247
|
-
|
248
246
|
if e['status'].empty? || e['status'].start_with?('4.0.0', '5.0.0')
|
249
247
|
# Check the value of D.S.N. in anotherset
|
250
|
-
as = Sisimai::SMTP::Status.find(anotherset['diagnosis'])
|
248
|
+
as = Sisimai::SMTP::Status.find(anotherset['diagnosis'])
|
251
249
|
if as.size > 0 && as[-4, 4] != '.0.0'
|
252
250
|
# The D.S.N. is neither an empty nor *.0.0
|
253
251
|
e['status'] = as
|
@@ -256,7 +254,7 @@ module Sisimai::Lhost
|
|
256
254
|
|
257
255
|
if e['replycode'].empty? || e['replycode'].end_with?('00')
|
258
256
|
# Check the value of SMTP reply code in $anotherset
|
259
|
-
ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis'])
|
257
|
+
ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis'])
|
260
258
|
if ar.size > 0 && ar.end_with?('00') == false
|
261
259
|
# The SMTP reply code is neither an empty nor *00
|
262
260
|
e['replycode'] = ar
|
@@ -278,8 +276,8 @@ module Sisimai::Lhost
|
|
278
276
|
|
279
277
|
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
|
280
278
|
e['command'] = commandset.shift || Sisimai::SMTP::Command.find(e['diagnosis'])
|
281
|
-
e['command']
|
282
|
-
e['spec']
|
279
|
+
e['command'] = 'HELO' if e["command"].empty? && e['diagnosis'].include?('refused to talk to me:')
|
280
|
+
e['spec'] = 'SMTP' if e["spec"].empty? && Sisimai::String.aligned(e['diagnosis'], ['host ', ' said:'])
|
283
281
|
end
|
284
282
|
|
285
283
|
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
data/lib/sisimai/lhost/qmail.rb
CHANGED
@@ -5,9 +5,23 @@ module Sisimai::Lhost
|
|
5
5
|
module Qmail
|
6
6
|
class << self
|
7
7
|
require 'sisimai/lhost'
|
8
|
+
require 'sisimai/string'
|
8
9
|
|
9
10
|
Indicators = Sisimai::Lhost.INDICATORS
|
10
|
-
Boundaries = [
|
11
|
+
Boundaries = [
|
12
|
+
# qmail-send.c:qmail_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" :...
|
13
|
+
"--- Below this line is a copy of the message.", # qmail-1.03
|
14
|
+
"--- Below this line is a copy of the mail header.",
|
15
|
+
"--- Below the next line is a copy of the message.", # The followings are the qmail clone
|
16
|
+
"--- Mensaje original adjunto.",
|
17
|
+
"Content-Type: message/rfc822",
|
18
|
+
"Original message follows.",
|
19
|
+
].freeze
|
20
|
+
RelayedVia = [["(qmail ", "invoked for bounce)"], ["(qmail ", "invoked from ", "network)"]].freeze
|
21
|
+
EmailTitle = [
|
22
|
+
"failure notice", # qmail-send.c:Subject: failure notice\n\
|
23
|
+
"Failure Notice", # Yahoo
|
24
|
+
].freeze
|
11
25
|
StartingOf = {
|
12
26
|
# qmail-remote.c:248| if (code >= 500) {
|
13
27
|
# qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
|
@@ -16,78 +30,97 @@ module Sisimai::Lhost
|
|
16
30
|
#
|
17
31
|
# Characters: K,Z,D in qmail-qmqpc.c, qmail-send.c, qmail-rspawn.c
|
18
32
|
# K = success, Z = temporary error, D = permanent error
|
19
|
-
error
|
20
|
-
message
|
21
|
-
|
33
|
+
"error" => ["Remote host said:"],
|
34
|
+
"message" => [
|
35
|
+
"Hi. This is the qmail", # qmail-send.c:Hi. This is the qmail-send program at ");
|
36
|
+
"He/Her is not ", # The followings are the qmail clone
|
37
|
+
"unable to deliver your message to the following addresses",
|
38
|
+
"Su mensaje no pudo ser entregado",
|
39
|
+
"Sorry, we were unable to deliver your message to the following address",
|
40
|
+
"This is the machine generated message from mail service",
|
41
|
+
"This is the mail delivery agent at",
|
42
|
+
"Unable to deliver message to the following address",
|
43
|
+
"unable to deliver your message to the following addresses",
|
44
|
+
"Unfortunately, your mail was not delivered to the following address:",
|
45
|
+
"Your mail message to the following address",
|
46
|
+
"Your message to the following addresses",
|
47
|
+
"We're sorry.",
|
48
|
+
],
|
49
|
+
"rhost" => ['Giving up on ', 'Connected to ', 'remote host '],
|
22
50
|
}.freeze
|
23
51
|
CommandSet = {
|
24
|
-
# Error text regular expressions which defined in qmail-remote.c
|
25
52
|
# qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
|
26
|
-
|
53
|
+
"CONN" => [" but greeting failed."],
|
27
54
|
# qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
|
28
|
-
|
55
|
+
"EHLO" => [" but my name was rejected."],
|
29
56
|
# qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
|
30
57
|
# reason = rejected
|
31
|
-
|
58
|
+
"MAIL" => [" but sender was rejected."],
|
32
59
|
# qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
|
33
60
|
# qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
|
34
61
|
# reason = userunknown
|
35
|
-
|
62
|
+
"RCPT" => [" does not like recipient."],
|
36
63
|
# qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
|
37
64
|
# qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
|
38
65
|
# qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
|
39
66
|
# qmail-remote.c:272| if (code >= 400) quit("Z"," failed after I sent the message");
|
40
|
-
|
67
|
+
"DATA" => [" failed on DATA command", " failed after I sent the message"],
|
41
68
|
}.freeze
|
42
69
|
|
43
70
|
# qmail-send.c:922| ... (&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
|
44
|
-
|
45
|
-
|
71
|
+
# qmail-remote-fallback.patch
|
72
|
+
HasExpired = "this message has been in the queue too long.".freeze
|
73
|
+
OnHoldPair = [" does not like recipient.", "this message has been in the queue too long."].freeze
|
46
74
|
FailOnLDAP = {
|
47
75
|
# qmail-ldap-1.03-20040101.patch:19817 - 19866
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
76
|
+
"exceedlimit" => ["The message exeeded the maximum size the user accepts"], # 5.2.3
|
77
|
+
"userunknown" => ["Sorry, no mailbox here by that name"], # 5.1.1
|
78
|
+
"suspend" => [ # 5.2.1
|
79
|
+
"Mailaddress is administrativly disabled",
|
80
|
+
"Mailaddress is administrativley disabled",
|
81
|
+
"Mailaddress is administratively disabled",
|
82
|
+
"Mailaddress is administrativeley disabled",
|
83
|
+
],
|
84
|
+
"systemerror" => [
|
85
|
+
"Automatic homedir creator crashed", # 4.3.0
|
86
|
+
"Illegal value in LDAP attribute", # 5.3.5
|
87
|
+
"LDAP attribute is not given but mandatory", # 5.3.5
|
88
|
+
"Timeout while performing search on LDAP server", # 4.4.3
|
89
|
+
"Too many results returned but needs to be unique", # 5.3.5
|
90
|
+
"Permanent error while executing qmail-forward", # 5.4.4
|
91
|
+
"Temporary error in automatic homedir creation", # 4.3.0 or 5.3.0
|
92
|
+
"Temporary error while executing qmail-forward", # 4.4.4
|
93
|
+
"Temporary failure in LDAP lookup", # 4.4.3
|
94
|
+
"Unable to contact LDAP server", # 4.4.3
|
95
|
+
"Unable to login into LDAP server, bad credentials",# 4.4.3
|
63
96
|
],
|
64
97
|
}.freeze
|
65
98
|
MessagesOf = {
|
66
|
-
# qmail-
|
67
|
-
# qmail-remote.c:
|
68
|
-
|
99
|
+
# qmail-remote.c:68| Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie();
|
100
|
+
# qmail-remote.c:78| Sorry, I couldn't find any host named ");
|
101
|
+
"hostunknown" => ["Sorry, I couldn't find any host "],
|
69
102
|
# error_str.c:192| X(EDQUOT,"disk quota exceeded")
|
70
|
-
|
103
|
+
"mailboxfull" => ["disk quota exceeded"],
|
71
104
|
# qmail-qmtpd.c:233| ... result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
|
72
105
|
# qmail-smtpd.c:391| ... out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return;
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
'systemfull' => ['Requested action not taken: mailbox unavailable (not enough free space)'],
|
78
|
-
'systemerror' => [
|
79
|
-
'bad interpreter: No such file or directory',
|
80
|
-
'system error',
|
81
|
-
'Unable to',
|
106
|
+
"mesgtoobig" => ["Message size exceeds fixed maximum message size:"],
|
107
|
+
"networkerror"=> [
|
108
|
+
"Sorry, I wasn't able to establish an SMTP connection",
|
109
|
+
"Sorry. Although I'm listed as a best-preference MX or A for that host",
|
82
110
|
],
|
83
|
-
|
84
|
-
|
85
|
-
|
111
|
+
"notaccept" => [
|
112
|
+
# notqmail 1.08 returns the following error message when the destination MX is NullMX
|
113
|
+
"Sorry, I couldn't find a mail exchanger or IP address",
|
86
114
|
],
|
87
|
-
|
88
|
-
|
89
|
-
|
115
|
+
"systemerror" => [
|
116
|
+
"bad interpreter: No such file or directory",
|
117
|
+
"system error",
|
118
|
+
"Unable to",
|
90
119
|
],
|
120
|
+
"systemfull" => ["Requested action not taken: mailbox unavailable (not enough free space)"],
|
121
|
+
# qmail-local.c:589| strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
|
122
|
+
# qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
|
123
|
+
"userunknown" => ["no mailbox here by that name"],
|
91
124
|
}.freeze
|
92
125
|
|
93
126
|
# @abstract Decodes the bounce message from qmail
|
@@ -100,16 +133,16 @@ module Sisimai::Lhost
|
|
100
133
|
# by qmail, see https://cr.yp.to/qmail.html
|
101
134
|
# e.g.) Received: (qmail 12345 invoked for bounce); 29 Apr 2009 12:34:56 -0000
|
102
135
|
# Subject: failure notice
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
mhead['received'].each do |e|
|
136
|
+
proceedsto = false
|
137
|
+
proceedsto = true if EmailTitle.any? { |a| mhead["subject"] == a }
|
138
|
+
mhead["received"].each do |e|
|
107
139
|
# Received: (qmail 2222 invoked for bounce);29 Apr 2017 23:34:45 +0900
|
108
140
|
# Received: (qmail 2202 invoked from network); 29 Apr 2018 00:00:00 +0900
|
109
|
-
|
141
|
+
proceedsto = true if RelayedVia.any? { |a| Sisimai::String.aligned(e, a) }
|
110
142
|
end
|
111
|
-
return nil
|
143
|
+
return nil if proceedsto == false
|
112
144
|
|
145
|
+
require "sisimai/smtp/command"
|
113
146
|
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
|
114
147
|
emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
|
115
148
|
bodyslices = emailparts[0].split("\n")
|
@@ -122,7 +155,7 @@ module Sisimai::Lhost
|
|
122
155
|
# line of the beginning of the original message.
|
123
156
|
if readcursor == 0
|
124
157
|
# Beginning of the bounce message or delivery status part
|
125
|
-
readcursor |= Indicators[:deliverystatus] if e.
|
158
|
+
readcursor |= Indicators[:deliverystatus] if StartingOf["message"].any? { |a| e.include?(a) }
|
126
159
|
next
|
127
160
|
end
|
128
161
|
next if (readcursor & Indicators[:deliverystatus]) == 0
|
@@ -134,31 +167,29 @@ module Sisimai::Lhost
|
|
134
167
|
# Giving up on 192.0.2.153.
|
135
168
|
v = dscontents[-1]
|
136
169
|
|
137
|
-
if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '
|
170
|
+
if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>:'])
|
138
171
|
# <kijitora@example.jp>:
|
139
|
-
if v[
|
172
|
+
if v["recipient"] != ""
|
140
173
|
# There are multiple recipient addresses in the message body.
|
141
174
|
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
142
175
|
v = dscontents[-1]
|
143
176
|
end
|
144
|
-
v[
|
177
|
+
v["recipient"] = Sisimai::Address.s3s4(e[e.index("<"), e.size])
|
145
178
|
recipients += 1
|
146
179
|
|
147
180
|
elsif dscontents.size == recipients
|
148
181
|
# Append error message
|
149
|
-
|
150
|
-
v[
|
151
|
-
v['diagnosis'] << e + ' '
|
152
|
-
v['alterrors'] = e if e.start_with?(StartingOf[:error][0])
|
182
|
+
v["diagnosis"] << e + " "
|
183
|
+
v["alterrors"] = e if e.start_with?(StartingOf["error"][0])
|
153
184
|
|
154
|
-
next if v[
|
155
|
-
StartingOf[
|
185
|
+
next if v["rhost"] != ""
|
186
|
+
StartingOf["rhost"].each do |r|
|
156
187
|
# Find a remote host name
|
157
188
|
p1 = e.index(r); next unless p1
|
158
189
|
cm = r.size
|
159
|
-
p2 = e.index(
|
190
|
+
p2 = e.index(" ", p1 + cm + 1) || p2 = e.rindex(".")
|
160
191
|
|
161
|
-
v[
|
192
|
+
v["rhost"] = Sisimai::String.sweep(e[p1 + cm, p2 - p1 - cm])
|
162
193
|
break
|
163
194
|
end
|
164
195
|
end
|
@@ -166,68 +197,61 @@ module Sisimai::Lhost
|
|
166
197
|
return nil unless recipients > 0
|
167
198
|
|
168
199
|
dscontents.each do |e|
|
169
|
-
e['
|
170
|
-
e[
|
171
|
-
|
172
|
-
unless e['command']
|
173
|
-
# Get the SMTP command name for the session
|
174
|
-
CommandSet.each_key do |r|
|
175
|
-
# Verify each regular expression of SMTP commands
|
176
|
-
next unless CommandSet[r].any? { |a| e['diagnosis'].include?(a) }
|
177
|
-
e['command'] = r.upcase
|
178
|
-
break
|
179
|
-
end
|
200
|
+
# Tidy up the error message in e['diagnosis'], Try to detect the bounce reason.
|
201
|
+
e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"])
|
180
202
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
203
|
+
# Get the SMTP command name for the session
|
204
|
+
CommandSet.each_key do |r|
|
205
|
+
# Get the last SMTP command
|
206
|
+
next unless CommandSet[r].any? { |a| e["diagnosis"].include?(a) }
|
207
|
+
e["command"] = r
|
208
|
+
break
|
209
|
+
end
|
210
|
+
|
211
|
+
if e["diagnosis"].include?("Sorry, no SMTP connection got far enough")
|
212
|
+
# Sorry, no SMTP connection got far enough; most progress was RCPT TO response; ...
|
213
|
+
e["command"] = Sisimai::SMTP::Command.find(e["diagnosis"]) if e["command"].empty?
|
185
214
|
end
|
186
215
|
|
187
216
|
# Detect the reason of bounce
|
188
|
-
if %w[HELO EHLO].index(e[
|
217
|
+
if %w[HELO EHLO].index(e["command"])
|
189
218
|
# HELO | Connected to 192.0.2.135 but my name was rejected.
|
190
|
-
e[
|
219
|
+
e["reason"] = "blocked"
|
191
220
|
else
|
192
221
|
# Try to match with each error message in the table
|
193
|
-
if Sisimai::String.aligned(e[
|
222
|
+
if Sisimai::String.aligned(e["diagnosis"], OnHoldPair)
|
194
223
|
# To decide the reason require pattern match with Sisimai::Reason::* modules
|
195
|
-
e[
|
224
|
+
e["reason"] = "onhold"
|
196
225
|
else
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
226
|
+
# Check that the error message includes any of message patterns or not
|
227
|
+
[e["alterrors"], e["diagnosis"]].each do |f|
|
228
|
+
# Try to detect an error reason
|
229
|
+
break if e["reason"] != ""
|
230
|
+
next unless f
|
231
|
+
MessagesOf.each_key do |r|
|
232
|
+
# The key is a bounce reason name
|
233
|
+
next unless MessagesOf[r].any? { |a| f.include?(a) }
|
234
|
+
e["reason"] = r
|
235
|
+
break
|
203
236
|
end
|
204
|
-
break if e[
|
205
|
-
|
206
|
-
next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
|
207
|
-
e['reason'] = r
|
208
|
-
break
|
209
|
-
end
|
237
|
+
break if e["reason"]
|
210
238
|
|
211
|
-
unless e['reason']
|
212
239
|
FailOnLDAP.each_key do |r|
|
213
|
-
#
|
214
|
-
next unless FailOnLDAP[r].any? { |a|
|
215
|
-
e[
|
240
|
+
# The key is a bounce reason name
|
241
|
+
next unless FailOnLDAP[r].any? { |a| f.include?(a) }
|
242
|
+
e["reason"] = r
|
216
243
|
break
|
217
244
|
end
|
218
|
-
|
219
|
-
|
220
|
-
unless e['reason']
|
221
|
-
e['reason'] = 'expired' if e['diagnosis'].include?(HasExpired)
|
245
|
+
break if e["reason"]
|
246
|
+
e["reason"] = "expired" if e["diagnosis"].include?(HasExpired)
|
222
247
|
end
|
223
248
|
end
|
224
249
|
end
|
225
250
|
|
226
|
-
e[
|
227
|
-
e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
|
251
|
+
e["command"] = Sisimai::SMTP::Command.find(e["diagnosis"]) if e["command"].empty?
|
228
252
|
end
|
229
253
|
|
230
|
-
return {
|
254
|
+
return { "ds" => dscontents, "rfc822" => emailparts[1] }
|
231
255
|
end
|
232
256
|
def description; return 'qmail'; end
|
233
257
|
end
|