sisimai 4.25.15-java → 5.0.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 +5 -5
- data/.travis.yml +3 -3
- data/ANALYTICAL-PRECISION +2 -2
- data/Benchmarks.mk +3 -3
- data/CONTRIBUTING +1 -1
- data/ChangeLog.md +419 -388
- data/Developers.mk +5 -6
- data/Gemfile +1 -1
- data/Makefile +15 -15
- data/README-JA.md +140 -78
- data/README.md +290 -143
- data/Rakefile +9 -3
- data/Repository.mk +2 -3
- data/lib/sisimai/address.rb +118 -74
- data/lib/sisimai/arf.rb +84 -82
- data/lib/sisimai/datetime.rb +5 -52
- data/lib/sisimai/{data → fact}/json.rb +7 -9
- data/lib/sisimai/fact/yaml.rb +31 -0
- data/lib/sisimai/fact.rb +468 -0
- data/lib/sisimai/lhost/activehunter.rb +12 -14
- data/lib/sisimai/lhost/amavis.rb +11 -14
- data/lib/sisimai/lhost/amazonses.rb +37 -41
- data/lib/sisimai/lhost/amazonworkmail.rb +15 -18
- data/lib/sisimai/lhost/aol.rb +12 -14
- data/lib/sisimai/lhost/apachejames.rb +19 -21
- data/lib/sisimai/lhost/barracuda.rb +10 -12
- data/lib/sisimai/lhost/bigfoot.rb +21 -21
- data/lib/sisimai/lhost/biglobe.rb +15 -16
- data/lib/sisimai/lhost/courier.rb +20 -20
- data/lib/sisimai/lhost/domino.rb +23 -19
- data/lib/sisimai/lhost/einsundeins.rb +23 -18
- data/lib/sisimai/lhost/exchange2003.rb +30 -29
- data/lib/sisimai/lhost/exchange2007.rb +70 -58
- data/lib/sisimai/lhost/exim.rb +175 -161
- data/lib/sisimai/lhost/ezweb.rb +31 -56
- data/lib/sisimai/lhost/facebook.rb +21 -33
- data/lib/sisimai/lhost/fml.rb +43 -48
- data/lib/sisimai/lhost/gmail.rb +29 -29
- data/lib/sisimai/lhost/gmx.rb +18 -17
- data/lib/sisimai/lhost/googlegroups.rb +9 -10
- data/lib/sisimai/lhost/gsuite.rb +21 -27
- data/lib/sisimai/lhost/imailserver.rb +25 -39
- data/lib/sisimai/lhost/interscanmss.rb +28 -31
- data/lib/sisimai/lhost/kddi.rb +22 -28
- data/lib/sisimai/lhost/mailfoundry.rb +11 -12
- data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
- data/lib/sisimai/lhost/mailru.rb +33 -27
- data/lib/sisimai/lhost/mcafee.rb +21 -31
- data/lib/sisimai/lhost/messagelabs.rb +17 -20
- data/lib/sisimai/lhost/messagingserver.rb +40 -37
- data/lib/sisimai/lhost/mfilter.rb +15 -16
- data/lib/sisimai/lhost/mxlogic.rb +24 -23
- data/lib/sisimai/lhost/notes.rb +17 -17
- data/lib/sisimai/lhost/office365.rb +63 -27
- data/lib/sisimai/lhost/opensmtpd.rb +12 -13
- data/lib/sisimai/lhost/outlook.rb +12 -15
- data/lib/sisimai/lhost/postfix.rb +179 -129
- data/lib/sisimai/lhost/powermta.rb +12 -14
- data/lib/sisimai/lhost/qmail.rb +44 -47
- data/lib/sisimai/lhost/receivingses.rb +15 -20
- data/lib/sisimai/lhost/sendgrid.rb +34 -32
- data/lib/sisimai/lhost/sendmail.rb +66 -53
- data/lib/sisimai/lhost/surfcontrol.rb +19 -19
- data/lib/sisimai/lhost/v5sendmail.rb +45 -39
- data/lib/sisimai/lhost/verizon.rb +35 -39
- data/lib/sisimai/lhost/x1.rb +18 -17
- data/lib/sisimai/lhost/x2.rb +17 -14
- data/lib/sisimai/lhost/x3.rb +19 -19
- data/lib/sisimai/lhost/x4.rb +72 -57
- data/lib/sisimai/lhost/x5.rb +17 -19
- data/lib/sisimai/lhost/x6.rb +41 -17
- data/lib/sisimai/lhost/yahoo.rb +17 -16
- data/lib/sisimai/lhost/yandex.rb +16 -20
- data/lib/sisimai/lhost/zoho.rb +16 -15
- data/lib/sisimai/lhost.rb +8 -10
- data/lib/sisimai/mail/maildir.rb +1 -3
- data/lib/sisimai/mail/mbox.rb +3 -4
- data/lib/sisimai/mail/memory.rb +0 -1
- data/lib/sisimai/mail/stdin.rb +1 -3
- data/lib/sisimai/mail.rb +3 -7
- data/lib/sisimai/mda.rb +28 -42
- data/lib/sisimai/message.rb +435 -313
- data/lib/sisimai/order.rb +5 -5
- data/lib/sisimai/reason/authfailure.rb +64 -0
- data/lib/sisimai/reason/badreputation.rb +53 -0
- data/lib/sisimai/reason/blocked.rb +94 -160
- data/lib/sisimai/reason/contenterror.rb +8 -9
- data/lib/sisimai/reason/delivered.rb +4 -6
- data/lib/sisimai/reason/exceedlimit.rb +10 -12
- data/lib/sisimai/reason/expired.rb +6 -8
- data/lib/sisimai/reason/feedback.rb +2 -3
- data/lib/sisimai/reason/filtered.rb +17 -19
- data/lib/sisimai/reason/hasmoved.rb +9 -10
- data/lib/sisimai/reason/hostunknown.rb +15 -15
- data/lib/sisimai/reason/mailboxfull.rb +10 -12
- data/lib/sisimai/reason/mailererror.rb +18 -20
- data/lib/sisimai/reason/mesgtoobig.rb +9 -11
- data/lib/sisimai/reason/networkerror.rb +5 -8
- data/lib/sisimai/reason/norelaying.rb +8 -11
- data/lib/sisimai/reason/notaccept.rb +13 -14
- data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
- data/lib/sisimai/reason/onhold.rb +6 -9
- data/lib/sisimai/reason/policyviolation.rb +14 -12
- data/lib/sisimai/reason/rejected.rb +26 -24
- data/lib/sisimai/reason/requireptr.rb +69 -0
- data/lib/sisimai/reason/securityerror.rb +33 -36
- data/lib/sisimai/reason/spamdetected.rb +114 -147
- data/lib/sisimai/reason/speeding.rb +49 -0
- data/lib/sisimai/reason/suspend.rb +11 -11
- data/lib/sisimai/reason/syntaxerror.rb +11 -10
- data/lib/sisimai/reason/systemerror.rb +7 -9
- data/lib/sisimai/reason/systemfull.rb +7 -8
- data/lib/sisimai/reason/toomanyconn.rb +9 -11
- data/lib/sisimai/reason/undefined.rb +2 -3
- data/lib/sisimai/reason/userunknown.rb +129 -146
- data/lib/sisimai/reason/vacation.rb +3 -4
- data/lib/sisimai/reason/virusdetected.rb +10 -11
- data/lib/sisimai/reason.rb +59 -64
- data/lib/sisimai/rfc1894.rb +55 -28
- data/lib/sisimai/rfc2045.rb +373 -0
- data/lib/sisimai/rfc3464.rb +250 -308
- data/lib/sisimai/rfc3834.rb +42 -47
- data/lib/sisimai/rfc5322.rb +75 -100
- data/lib/sisimai/rfc5965.rb +31 -0
- data/lib/sisimai/rhost/cox.rb +5 -6
- data/lib/sisimai/rhost/franceptt.rb +6 -8
- data/lib/sisimai/rhost/godaddy.rb +12 -12
- data/lib/sisimai/rhost/{googleapps.rb → google.rb} +80 -72
- data/lib/sisimai/rhost/iua.rb +9 -10
- data/lib/sisimai/rhost/kddi.rb +6 -8
- data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
- data/lib/sisimai/rhost/mimecast.rb +42 -40
- data/lib/sisimai/rhost/nttdocomo.rb +12 -12
- data/lib/sisimai/rhost/spectrum.rb +10 -12
- data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
- data/lib/sisimai/rhost.rb +23 -31
- data/lib/sisimai/smtp/command.rb +59 -0
- data/lib/sisimai/smtp/error.rb +4 -7
- data/lib/sisimai/smtp/reply.rb +161 -74
- data/lib/sisimai/smtp/status.rb +504 -393
- data/lib/sisimai/smtp/transcript.rb +124 -0
- data/lib/sisimai/smtp.rb +0 -1
- data/lib/sisimai/string.rb +74 -5
- data/lib/sisimai/time.rb +1 -2
- data/lib/sisimai/version.rb +1 -1
- data/lib/sisimai.rb +35 -21
- data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
- data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
- data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
- data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
- data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
- data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
- data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
- data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
- data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
- data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
- data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
- data/sisimai-java.gemspec +1 -1
- data/sisimai.gemspec +1 -1
- metadata +42 -22
- data/.rspec +0 -2
- data/lib/sisimai/data/yaml.rb +0 -33
- data/lib/sisimai/data.rb +0 -411
- data/lib/sisimai/mime.rb +0 -456
- /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
data/lib/sisimai/message.rb
CHANGED
|
@@ -1,353 +1,475 @@
|
|
|
1
1
|
module Sisimai
|
|
2
|
-
# Sisimai::Message convert bounce email text to data structure. It resolve
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
2
|
+
# Sisimai::Message convert bounce email text to data structure. It resolve email text into an UNIX
|
|
3
|
+
# From line, the header part of the mail, delivery status, and RFC822 header part. When the email
|
|
4
|
+
# given as a argument of "rise" method is not a bounce email, the method returns nil.
|
|
5
|
+
module Message
|
|
6
|
+
class << self
|
|
7
|
+
require 'sisimai/rfc1894'
|
|
8
|
+
require 'sisimai/rfc2045'
|
|
9
|
+
require 'sisimai/rfc5322'
|
|
10
|
+
require 'sisimai/rfc5965'
|
|
11
|
+
require 'sisimai/address'
|
|
12
|
+
require 'sisimai/string'
|
|
13
|
+
require 'sisimai/order'
|
|
14
|
+
require 'sisimai/lhost'
|
|
15
|
+
|
|
16
|
+
DefaultSet = Sisimai::Order.another.freeze
|
|
17
|
+
LhostTable = Sisimai::Lhost.path.freeze
|
|
18
|
+
Fields1894 = Sisimai::RFC1894.FIELDINDEX.freeze
|
|
19
|
+
Fields5322 = Sisimai::RFC5322.FIELDINDEX.freeze
|
|
20
|
+
Fields5965 = Sisimai::RFC5965.FIELDINDEX.freeze
|
|
21
|
+
FieldIndex = [Fields1894.flatten, Fields5322.flatten, Fields5965.flatten].flatten.freeze
|
|
22
|
+
FieldTable = FieldIndex.map { |e| [e.downcase, e] }.to_h.freeze
|
|
23
|
+
ReplacesAs = { 'Content-Type' => [%w[message/xdelivery-status message/delivery-status]] }.freeze
|
|
24
|
+
Boundaries = ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
|
|
25
|
+
|
|
26
|
+
# Read an email message and convert to structured format
|
|
27
|
+
# @param [Hash] argvs Module to be loaded
|
|
28
|
+
# @options argvs [String] :data Entire email message
|
|
29
|
+
# @options argvs [Array] :load User defined MTA module list
|
|
30
|
+
# @options argvs [Array] :order The order of MTA modules
|
|
31
|
+
# @options argvs [Code] :hook Reference to callback method
|
|
32
|
+
# @return [Sisimai::Message] Structured email data or nil if each
|
|
33
|
+
# value of the arguments are missing
|
|
34
|
+
def rise(**argvs)
|
|
35
|
+
return nil unless argvs
|
|
36
|
+
email = argvs[:data].scrub('?').gsub("\r\n", "\n")
|
|
37
|
+
thing = { 'from' => '','header' => {}, 'rfc822' => '', 'ds' => [], 'catch' => nil }
|
|
38
|
+
param = {}
|
|
39
|
+
|
|
40
|
+
# 0. Load specified MTA modules
|
|
41
|
+
[:load, :order].each do |e|
|
|
42
|
+
# Order of MTA modules
|
|
43
|
+
next unless argvs[e]
|
|
44
|
+
next unless argvs[e].is_a? Array
|
|
45
|
+
next if argvs[e].empty?
|
|
46
|
+
param[e.to_s] = argvs[e]
|
|
47
|
+
end
|
|
48
|
+
tobeloaded = Sisimai::Message.load(param)
|
|
49
|
+
|
|
50
|
+
aftersplit = nil
|
|
51
|
+
beforefact = nil
|
|
52
|
+
parseagain = 0
|
|
53
|
+
|
|
54
|
+
while parseagain < 2 do
|
|
55
|
+
# 1. Split email data to headers and a body part.
|
|
56
|
+
break unless aftersplit = Sisimai::Message.part(email)
|
|
57
|
+
|
|
58
|
+
# 2. Convert email headers from text to hash reference
|
|
59
|
+
thing['from'] = aftersplit[0]
|
|
60
|
+
thing['header'] = Sisimai::Message.makemap(aftersplit[1])
|
|
61
|
+
|
|
62
|
+
# 3. Decode and rewrite the "Subject:" header
|
|
63
|
+
unless thing['header']['subject'].empty?
|
|
64
|
+
# Decode MIME-Encoded "Subject:" header
|
|
65
|
+
cv = thing['header']['subject']
|
|
66
|
+
cq = Sisimai::RFC2045.is_encoded(cv) ? Sisimai::RFC2045.decodeH(cv.split(/[ ]/)) : cv
|
|
67
|
+
cl = cq.downcase
|
|
68
|
+
p1 = cl.index('fwd:'); p1 = cl.index('fw:') unless p1
|
|
69
|
+
|
|
70
|
+
# Remove "Fwd:" string from the Subject: header
|
|
71
|
+
if p1
|
|
72
|
+
# Delete quoted strings, quote symbols(>)
|
|
73
|
+
cq = Sisimai::String.sweep(cq[cq.index(':') + 1, cq.size])
|
|
74
|
+
aftersplit[2] = aftersplit[2].gsub(/^[>]+[ ]/, '').gsub(/^[>]$/, '')
|
|
75
|
+
end
|
|
76
|
+
thing['header']['subject'] = cq
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# 4. Rewrite message body for detecting the bounce reason
|
|
80
|
+
param = {
|
|
81
|
+
'hook' => argvs[:hook] || nil,
|
|
82
|
+
'mail' => thing,
|
|
83
|
+
'body' => aftersplit[2],
|
|
84
|
+
'tobeloaded' => tobeloaded,
|
|
85
|
+
'tryonfirst' => Sisimai::Order.make(thing['header']['subject'])
|
|
86
|
+
}
|
|
87
|
+
break if beforefact = Sisimai::Message.sift(param)
|
|
88
|
+
break unless Boundaries.any? { |a| aftersplit[2].include?(a) }
|
|
89
|
+
|
|
90
|
+
# 5. Try to sift again
|
|
91
|
+
# There is a bounce message inside of mutipart/*, try to sift the first message/rfc822
|
|
92
|
+
# part as a entire message body again.
|
|
93
|
+
parseagain += 1
|
|
94
|
+
email = Sisimai::RFC5322.part(aftersplit[2], Boundaries, true).pop.sub(/\A[\r\n\s]+/, '')
|
|
95
|
+
break unless email.size > 128
|
|
68
96
|
end
|
|
69
|
-
|
|
97
|
+
return nil unless beforefact
|
|
98
|
+
return nil if beforefact.empty?
|
|
99
|
+
|
|
100
|
+
# 6. Rewrite headers of the original message in the body part
|
|
101
|
+
%w|ds catch rfc822|.each { |e| thing[e] = beforefact[e] }
|
|
102
|
+
p = beforefact['rfc822']
|
|
103
|
+
p = aftersplit[2] if p.empty?
|
|
104
|
+
thing['rfc822'] = p.is_a?(::String) ? Sisimai::Message.makemap(p, true) : p
|
|
105
|
+
|
|
106
|
+
return thing
|
|
70
107
|
end
|
|
71
108
|
|
|
72
|
-
#
|
|
73
|
-
param
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
@header = thing['header']
|
|
91
|
-
@ds = thing['ds']
|
|
92
|
-
@rfc822 = thing['rfc822']
|
|
93
|
-
@catch = thing['catch'] || nil
|
|
94
|
-
end
|
|
109
|
+
# Load MTA modules which specified at 'order' and 'load' in the argument
|
|
110
|
+
# @param [Hash] argvs Module information to be loaded
|
|
111
|
+
# @options argvs [Array] load User defined MTA module list
|
|
112
|
+
# @options argvs [Array] order The order of MTA modules
|
|
113
|
+
# @return [Array] Module list
|
|
114
|
+
# @since v4.20.0
|
|
115
|
+
def load(argvs)
|
|
116
|
+
modulelist = []
|
|
117
|
+
tobeloaded = []
|
|
118
|
+
|
|
119
|
+
%w[load order].each do |e|
|
|
120
|
+
# The order of MTA modules specified by user
|
|
121
|
+
next unless argvs[e]
|
|
122
|
+
next unless argvs[e].is_a? Array
|
|
123
|
+
next if argvs[e].empty?
|
|
124
|
+
|
|
125
|
+
modulelist += argvs['order'] if e == 'order'
|
|
126
|
+
next unless e == 'load'
|
|
95
127
|
|
|
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
|
-
|
|
100
|
-
# Load MTA modules which specified at 'order' and 'load' in the argument
|
|
101
|
-
# @param [Hash] argvs Module information to be loaded
|
|
102
|
-
# @options argvs [Array] load User defined MTA module list
|
|
103
|
-
# @options argvs [Array] order The order of MTA modules
|
|
104
|
-
# @return [Array] Module list
|
|
105
|
-
# @since v4.20.0
|
|
106
|
-
def self.load(argvs)
|
|
107
|
-
modulelist = []
|
|
108
|
-
tobeloaded = []
|
|
109
|
-
|
|
110
|
-
%w[load order].each do |e|
|
|
111
|
-
# The order of MTA modules specified by user
|
|
112
|
-
next unless argvs[e]
|
|
113
|
-
next unless argvs[e].is_a? Array
|
|
114
|
-
next if argvs[e].empty?
|
|
115
|
-
|
|
116
|
-
modulelist += argvs['order'] if e == 'order'
|
|
117
|
-
next unless e == 'load'
|
|
118
|
-
|
|
119
|
-
# Load user defined MTA module
|
|
120
|
-
argvs['load'].each do |v|
|
|
121
128
|
# Load user defined MTA module
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
argvs['load'].each do |v|
|
|
130
|
+
# Load user defined MTA module
|
|
131
|
+
begin
|
|
132
|
+
require v.to_s.gsub('::', '/').downcase
|
|
133
|
+
rescue LoadError
|
|
134
|
+
warn ' ***warning: Failed to load ' << v
|
|
135
|
+
next
|
|
136
|
+
end
|
|
137
|
+
tobeloaded << v
|
|
127
138
|
end
|
|
128
|
-
tobeloaded << v
|
|
129
139
|
end
|
|
130
|
-
end
|
|
131
140
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
141
|
+
while e = modulelist.shift do
|
|
142
|
+
# Append the custom order of MTA modules
|
|
143
|
+
next if tobeloaded.index(e)
|
|
144
|
+
tobeloaded << e
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
return tobeloaded
|
|
136
148
|
end
|
|
137
149
|
|
|
138
|
-
|
|
139
|
-
|
|
150
|
+
# Divide email data up headers and a body part.
|
|
151
|
+
# @param [String] email Email data
|
|
152
|
+
# @return [Array] Email data after split
|
|
153
|
+
def part(email)
|
|
154
|
+
return nil if email.empty?
|
|
140
155
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def self.divideup(email)
|
|
145
|
-
return nil if email.empty?
|
|
146
|
-
|
|
147
|
-
block = ['', '', ''] # 0:From, 1:Header, 2:Body
|
|
148
|
-
email.gsub!(/\r\n/, "\n") if email.include?("\r\n")
|
|
149
|
-
email.gsub!(/[ \t]+$/, '') if email =~ /[ \t]+$/
|
|
150
|
-
|
|
151
|
-
(block[1], block[2]) = email.split(/\n\n/, 2)
|
|
152
|
-
return nil unless block[1]
|
|
153
|
-
return nil unless block[2]
|
|
154
|
-
|
|
155
|
-
if block[1].start_with?('From ')
|
|
156
|
-
# From MAILER-DAEMON Tue Feb 11 00:00:00 2014
|
|
157
|
-
block[0] = block[1].split(/\n/, 2)[0].delete("\r")
|
|
158
|
-
else
|
|
159
|
-
# Set pseudo UNIX From line
|
|
160
|
-
block[0] = 'MAILER-DAEMON Tue Feb 11 00:00:00 2014'
|
|
161
|
-
end
|
|
156
|
+
parts = ['', '', ''] # 0:From, 1:Header, 2:Body
|
|
157
|
+
email.gsub!(/\A\s+/, '')
|
|
158
|
+
email.gsub!(/\r\n/, "\n") if email.include?("\r\n")
|
|
162
159
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
end
|
|
160
|
+
(parts[1], parts[2]) = email.split(/\n\n/, 2)
|
|
161
|
+
return nil unless parts[1]
|
|
162
|
+
return nil unless parts[2]
|
|
167
163
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# @return [Hash] Structured email header data
|
|
172
|
-
# @since v4.25.6
|
|
173
|
-
def self.makemap(argv0 = '', argv1 = nil)
|
|
174
|
-
return {} if argv0.empty?
|
|
175
|
-
argv0.gsub!(/^[>]+[ ]/m, '') # Remove '>' indent symbol of forwarded message
|
|
176
|
-
|
|
177
|
-
# Select and convert all the headers in $argv0. The following regular expression
|
|
178
|
-
# is based on https://gist.github.com/xtetsuji/b080e1f5551d17242f6415aba8a00239
|
|
179
|
-
headermaps = { 'subject' => '' }
|
|
180
|
-
recvheader = []
|
|
181
|
-
argv0.scan(/^([\w-]+):[ ]*(.*?)\n(?![\s\t])/m) { |e| headermaps[e[0].downcase] = e[1] }
|
|
182
|
-
headermaps.delete('received')
|
|
183
|
-
headermaps.each_key { |e| headermaps[e].gsub!(/\n[\s\t]+/, ' ') }
|
|
184
|
-
|
|
185
|
-
if argv0.include?('Received:')
|
|
186
|
-
# Capture values of each Received: header
|
|
187
|
-
recvheader = argv0.scan(/^Received:[ ]*(.*?)\n(?![\s\t])/m).flatten
|
|
188
|
-
recvheader.each { |e| e.gsub!(/\n[\s\t]+/, ' ') }
|
|
189
|
-
end
|
|
190
|
-
headermaps['received'] = recvheader
|
|
191
|
-
|
|
192
|
-
return headermaps unless argv1
|
|
193
|
-
return headermaps if headermaps['subject'].empty?
|
|
194
|
-
|
|
195
|
-
# Convert MIME-Encoded subject
|
|
196
|
-
if Sisimai::String.is_8bit(headermaps['subject'])
|
|
197
|
-
# The value of ``Subject'' header is including multibyte character,
|
|
198
|
-
# is not MIME-Encoded text.
|
|
199
|
-
headermaps['subject'].scrub!('?')
|
|
200
|
-
else
|
|
201
|
-
# MIME-Encoded subject field or ASCII characters only
|
|
202
|
-
r = []
|
|
203
|
-
if Sisimai::MIME.is_mimeencoded(headermaps['subject'])
|
|
204
|
-
# split the value of Subject by borderline
|
|
205
|
-
headermaps['subject'].split(/ /).each do |v|
|
|
206
|
-
# Insert value to the array if the string is MIME encoded text
|
|
207
|
-
r << v if Sisimai::MIME.is_mimeencoded(v)
|
|
208
|
-
end
|
|
164
|
+
if parts[1].start_with?('From ')
|
|
165
|
+
# From MAILER-DAEMON Tue Feb 11 00:00:00 2014
|
|
166
|
+
parts[0] = parts[1].split(/\n/, 2)[0].delete("\r")
|
|
209
167
|
else
|
|
210
|
-
#
|
|
211
|
-
|
|
168
|
+
# Set pseudo UNIX From line
|
|
169
|
+
parts[0] = 'MAILER-DAEMON Tue Feb 11 00:00:00 2014'
|
|
170
|
+
end
|
|
171
|
+
parts[1] << "\n" unless parts[1].end_with?("\n")
|
|
172
|
+
|
|
173
|
+
%w[image/ application/ text/html].each do |e|
|
|
174
|
+
# https://github.com/sisimai/p5-sisimai/issues/492, Reduce email size
|
|
175
|
+
p0 = 0
|
|
176
|
+
p1 = 0
|
|
177
|
+
ep = e == 'text/html' ? '</html>' : "--\n"
|
|
178
|
+
while true
|
|
179
|
+
# Remove each part from "Content-Type: image/..." to "--\n" (the end of each boundary)
|
|
180
|
+
p0 = parts[2].index('Content-Type: ' + e, p0); break unless p0
|
|
181
|
+
p1 = parts[2].index(ep, p0 + 32); break unless p1
|
|
182
|
+
parts[2][p0, p1 - p0] = ''
|
|
183
|
+
end
|
|
212
184
|
end
|
|
213
|
-
|
|
185
|
+
parts[2] << "\n"
|
|
186
|
+
return parts
|
|
214
187
|
end
|
|
215
|
-
return headermaps
|
|
216
|
-
end
|
|
217
188
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
# PRECHECK_EACH_HEADER:
|
|
240
|
-
# Set empty string if the value is nil
|
|
241
|
-
mailheader['from'] ||= ''
|
|
242
|
-
mailheader['subject'] ||= ''
|
|
243
|
-
mailheader['content-type'] ||= ''
|
|
244
|
-
|
|
245
|
-
# Decode BASE64 Encoded message body, rewrite.
|
|
246
|
-
mesgformat = (mailheader['content-type'] || '').downcase
|
|
247
|
-
ctencoding = (mailheader['content-transfer-encoding'] || '').downcase
|
|
248
|
-
if mesgformat.start_with?('text/')
|
|
249
|
-
# Content-Type: text/plain; charset=UTF-8
|
|
250
|
-
if ctencoding == 'base64'
|
|
251
|
-
# Content-Transfer-Encoding: base64
|
|
252
|
-
bodystring = Sisimai::MIME.base64d(bodystring)
|
|
253
|
-
|
|
254
|
-
elsif ctencoding == 'quoted-printable'
|
|
255
|
-
# Content-Transfer-Encoding: quoted-printable
|
|
256
|
-
bodystring = Sisimai::MIME.qprintd(bodystring)
|
|
189
|
+
# Convert a text including email headers to a hash reference
|
|
190
|
+
# @param [String] argv0 Email header data
|
|
191
|
+
# @param [Bool] argv1 Decode "Subject:" header
|
|
192
|
+
# @return [Hash] Structured email header data
|
|
193
|
+
# @since v4.25.6
|
|
194
|
+
def makemap(argv0 = '', argv1 = nil)
|
|
195
|
+
return {} if argv0.empty?
|
|
196
|
+
argv0.gsub!(/^[>]+[ ]/m, '') # Remove '>' indent symbol of forwarded message
|
|
197
|
+
|
|
198
|
+
# Select and convert all the headers in $argv0. The following regular expression is based on
|
|
199
|
+
# https://gist.github.com/xtetsuji/b080e1f5551d17242f6415aba8a00239
|
|
200
|
+
headermaps = { 'subject' => '' }
|
|
201
|
+
recvheader = []
|
|
202
|
+
argv0.scan(/^([\w-]+):[ ]*(.*?)\n(?![\s\t])/m) { |e| headermaps[e[0].downcase] = e[1] }
|
|
203
|
+
headermaps.delete('received')
|
|
204
|
+
headermaps.each_key { |e| headermaps[e].gsub!(/\n[\s\t]+/, ' ') }
|
|
205
|
+
|
|
206
|
+
if argv0.include?('Received:')
|
|
207
|
+
# Capture values of each Received: header
|
|
208
|
+
recvheader = argv0.scan(/^Received:[ ]*(.*?)\n(?![\s\t])/m).flatten
|
|
209
|
+
recvheader.each { |e| e.gsub!(/\n[\s\t]+/, ' ') }
|
|
257
210
|
end
|
|
211
|
+
headermaps['received'] = recvheader
|
|
258
212
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
havecaught = hookmethod.call(p)
|
|
281
|
-
rescue StandardError => ce
|
|
282
|
-
warn ' ***warning: Something is wrong in hook method :' << ce.to_s
|
|
213
|
+
return headermaps unless argv1
|
|
214
|
+
return headermaps if headermaps['subject'].empty?
|
|
215
|
+
|
|
216
|
+
# Convert MIME-Encoded subject
|
|
217
|
+
if Sisimai::String.is_8bit(headermaps['subject'])
|
|
218
|
+
# The value of ``Subject'' header is including multibyte character, is not MIME-Encoded text.
|
|
219
|
+
headermaps['subject'].scrub!('?')
|
|
220
|
+
else
|
|
221
|
+
# MIME-Encoded subject field or ASCII characters only
|
|
222
|
+
r = []
|
|
223
|
+
if Sisimai::RFC2045.is_encoded(headermaps['subject'])
|
|
224
|
+
# split the value of Subject by borderline
|
|
225
|
+
headermaps['subject'].split(/ /).each do |v|
|
|
226
|
+
# Insert value to the array if the string is MIME encoded text
|
|
227
|
+
r << v if Sisimai::RFC2045.is_encoded(v)
|
|
228
|
+
end
|
|
229
|
+
else
|
|
230
|
+
# Subject line is not MIME encoded
|
|
231
|
+
r << headermaps['subject']
|
|
232
|
+
end
|
|
233
|
+
headermaps['subject'] = Sisimai::RFC2045.decodeH(r)
|
|
283
234
|
end
|
|
235
|
+
return headermaps
|
|
284
236
|
end
|
|
285
237
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
238
|
+
# @abstract Tidy up each field name and format
|
|
239
|
+
# @param [String] argv0 Strings including field and value used at an email
|
|
240
|
+
# @return [String] Strings tidied up
|
|
241
|
+
# @since v5.0.0
|
|
242
|
+
def tidy(argv0 = '')
|
|
243
|
+
return '' if argv0.empty?
|
|
244
|
+
|
|
245
|
+
email = ''
|
|
246
|
+
argv0.split("\n").each do |e|
|
|
247
|
+
# Find and tidy up fields defined in RFC5322, RFC1894, and RFC5965
|
|
248
|
+
# 1. Find a field label defined in RFC5322, RFC1894, or RFC5965 from this line
|
|
249
|
+
p0 = e.index(':') || 0
|
|
250
|
+
cf = e.downcase[0, p0]
|
|
251
|
+
|
|
252
|
+
unless FieldTable.has_key?(cf)
|
|
253
|
+
email << e + "\n"
|
|
254
|
+
next
|
|
301
255
|
end
|
|
302
256
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
257
|
+
# 2. There is a field label defined in RFC5322, RFC1894, or RFC5965 from this line.
|
|
258
|
+
# Code below replaces the field name with a valid name listed in @fieldindex when
|
|
259
|
+
# the field name does not match with a valid name.
|
|
260
|
+
# - Before: Message-id: <...>
|
|
261
|
+
# - After: Message-Id: <...>
|
|
262
|
+
fieldlabel = FieldTable[cf]
|
|
263
|
+
substring0 = e[0, p0]
|
|
264
|
+
e[0, p0] = fieldlabel unless substring0.empty?
|
|
265
|
+
|
|
266
|
+
# 3. There is no " " (space character) immediately after ":"
|
|
267
|
+
# - before: Content-Type:text/plain
|
|
268
|
+
# - After: Content-Type: text/plain
|
|
269
|
+
substring0 = e[p0 + 1, 1]
|
|
270
|
+
e[p0, 1] = ': ' if substring0 != ' '
|
|
271
|
+
|
|
272
|
+
# 4. Remove redundant space characters after ":"
|
|
273
|
+
while true
|
|
274
|
+
# - Before: Message-Id: <...>
|
|
275
|
+
# - After: Message-Id: <...>
|
|
276
|
+
break unless p0 + 2 < e.size
|
|
277
|
+
break unless e[p0 + 2, 1] == ' '
|
|
278
|
+
e[p0 + 2, 1] = ''
|
|
311
279
|
end
|
|
312
280
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
281
|
+
# 5. Tidy up a sub type of each field defined in RFC1894 such as Reporting-MTA: DNS;...
|
|
282
|
+
p1 = e.index(';') || -1
|
|
283
|
+
while true
|
|
284
|
+
# Such as Diagnostic-Code, Remote-MTA, and so on
|
|
285
|
+
# - Before: Diagnostic-Code: SMTP;550 User unknown
|
|
286
|
+
# - After: Diagnostic-Code: smtp; 550 User unknown
|
|
287
|
+
break unless p1 > p0
|
|
288
|
+
break unless ['Content-Type'].concat(Fields1894).any? { |a| a.start_with?(fieldlabel) }
|
|
289
|
+
|
|
290
|
+
substring0 = e[p0 + 2, p1 - p0 - 1]
|
|
291
|
+
e[p0 + 2, substring0.size] = substring0.downcase + ' '
|
|
292
|
+
break
|
|
320
293
|
end
|
|
321
294
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
295
|
+
# 6. Remove redundant space characters after ";"
|
|
296
|
+
while true
|
|
297
|
+
# - Before: Diagnostic-Code: SMTP; 550 User unknown
|
|
298
|
+
# - After: Diagnostic-Code: SMTP; 550 User unknown
|
|
299
|
+
break unless p1 + 2 < e.size
|
|
300
|
+
break unless e[p1 + 2, 1] == ' '
|
|
301
|
+
e[p1 + 2, 1] = ''
|
|
327
302
|
end
|
|
328
303
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
304
|
+
# 7. Tidy up a value, and a parameter of Content-Type: field
|
|
305
|
+
while true
|
|
306
|
+
# Replace the value of "Content-Type" field
|
|
307
|
+
break unless ReplacesAs.has_key?(fieldlabel)
|
|
308
|
+
p2 = 0
|
|
309
|
+
|
|
310
|
+
ReplacesAs[fieldlabel].each do |f|
|
|
311
|
+
# Content-Type: message/xdelivery-status
|
|
312
|
+
p2 = e.index(f[0]) || -1
|
|
313
|
+
next unless p2 > 1
|
|
314
|
+
|
|
315
|
+
e[p2, f[0].size] = f[1]
|
|
316
|
+
p1 = e.index(';')
|
|
317
|
+
break
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# A parameter name of Content-Type field should be a lower-cased string
|
|
321
|
+
# - Before: Content-Type: text/plain; CharSet=ascii; Boundary=...
|
|
322
|
+
# - After: Content-Type: text/plain; charset=ascii; boundary=...
|
|
323
|
+
break unless fieldlabel == 'Content-Type'
|
|
324
|
+
p2 = e.index('=') || -1
|
|
325
|
+
break unless p2 > 0
|
|
326
|
+
break unless p2 > p1
|
|
327
|
+
|
|
328
|
+
substring0 = e[p1 + 2, p2 - p1 - 2]
|
|
329
|
+
e[p1 + 2, p2 - p1 - 2] = substring0.downcase
|
|
330
|
+
break
|
|
335
331
|
end
|
|
336
|
-
|
|
337
|
-
break # as of now, we have no sample email for coding this block
|
|
332
|
+
email << e + "\n"
|
|
338
333
|
end
|
|
334
|
+
|
|
335
|
+
email << "\n" unless email.end_with?("\n\n")
|
|
336
|
+
return email
|
|
339
337
|
end
|
|
340
|
-
return nil unless parseddata
|
|
341
338
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
339
|
+
# @abstract Sift bounce mail with each MTA module
|
|
340
|
+
# @param [Hash] argvs Processing message entity.
|
|
341
|
+
# @param options argvs [Hash] mail Email message entity
|
|
342
|
+
# @param options mail [String] from From line of mbox
|
|
343
|
+
# @param options mail [Hash] header Email header data
|
|
344
|
+
# @param options mail [String] rfc822 Original message part
|
|
345
|
+
# @param options mail [Array] ds Delivery status list(parsed data)
|
|
346
|
+
# @param options argvs [String] body Email message body
|
|
347
|
+
# @param options argvs [Array] tryonfirst MTA module list to load on first
|
|
348
|
+
# @param options argvs [Array] tobeloaded User defined MTA module list
|
|
349
|
+
# @return [Hash] Parsed and structured bounce mails
|
|
350
|
+
def sift(argvs)
|
|
351
|
+
return nil unless argvs['mail']
|
|
352
|
+
return nil unless argvs['body']
|
|
353
|
+
|
|
354
|
+
mailheader = argvs['mail']['header']
|
|
355
|
+
bodystring = argvs['body']
|
|
356
|
+
hookmethod = argvs['hook'] || nil
|
|
357
|
+
havecaught = nil
|
|
358
|
+
return nil unless mailheader
|
|
359
|
+
|
|
360
|
+
# PRECHECK_EACH_HEADER:
|
|
361
|
+
# Set empty string if the value is nil
|
|
362
|
+
mailheader['from'] ||= ''
|
|
363
|
+
mailheader['subject'] ||= ''
|
|
364
|
+
mailheader['content-type'] ||= ''
|
|
365
|
+
|
|
366
|
+
# Tidy up each field name and value in the entire message body
|
|
367
|
+
bodystring = Sisimai::Message.tidy(bodystring)
|
|
368
|
+
|
|
369
|
+
# Decode BASE64 Encoded message body, rewrite.
|
|
370
|
+
mesgformat = (mailheader['content-type'] || '').downcase
|
|
371
|
+
ctencoding = (mailheader['content-transfer-encoding'] || '').downcase
|
|
372
|
+
if mesgformat.start_with?('text/plain', 'text/html')
|
|
373
|
+
# Content-Type: text/plain; charset=UTF-8
|
|
374
|
+
if ctencoding == 'base64'
|
|
375
|
+
# Content-Transfer-Encoding: base64
|
|
376
|
+
bodystring = Sisimai::RFC2045.decodeB(bodystring)
|
|
377
|
+
|
|
378
|
+
elsif ctencoding == 'quoted-printable'
|
|
379
|
+
# Content-Transfer-Encoding: quoted-printable
|
|
380
|
+
bodystring = Sisimai::RFC2045.decodeQ(bodystring)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
if mesgformat.start_with?('text/html;')
|
|
384
|
+
# Content-Type: text/html;...
|
|
385
|
+
bodystring = Sisimai::String.to_plain(bodystring, true)
|
|
386
|
+
end
|
|
387
|
+
elsif mesgformat.start_with?('multipart/')
|
|
388
|
+
# NOT text/plain
|
|
389
|
+
# In case of Content-Type: multipart/*
|
|
390
|
+
p = Sisimai::RFC2045.makeflat(mailheader['content-type'], bodystring)
|
|
391
|
+
bodystring = p unless p.empty?
|
|
392
|
+
end
|
|
393
|
+
bodystring = bodystring.scrub('?').delete("\r").gsub("\t", " ")
|
|
394
|
+
|
|
395
|
+
haveloaded = {}
|
|
396
|
+
havesifted = nil
|
|
397
|
+
modulename = ''
|
|
398
|
+
if hookmethod.is_a? Proc
|
|
399
|
+
# Call the hook method
|
|
400
|
+
begin
|
|
401
|
+
p = { 'headers' => mailheader, 'message' => bodystring }
|
|
402
|
+
havecaught = hookmethod.call(p)
|
|
403
|
+
rescue StandardError => ce
|
|
404
|
+
warn ' ***warning: Something is wrong in hook method ":hook":' << ce.to_s
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
catch :PARSER do
|
|
409
|
+
while true
|
|
410
|
+
# 1. User-Defined Module
|
|
411
|
+
# 2. MTA Module Candidates to be tried on first
|
|
412
|
+
# 3. Sisimai::Lhost::*
|
|
413
|
+
# 4. Sisimai::RFC3464
|
|
414
|
+
# 5. Sisimai::ARF
|
|
415
|
+
# 6. Sisimai::RFC3834
|
|
416
|
+
while r = argvs['tobeloaded'].shift do
|
|
417
|
+
# Call user defined MTA modules
|
|
418
|
+
next if haveloaded[r]
|
|
419
|
+
havesifted = Module.const_get(r).inquire(mailheader, bodystring)
|
|
420
|
+
haveloaded[r] = true
|
|
421
|
+
modulename = r
|
|
422
|
+
throw :PARSER if havesifted
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
[argvs['tryonfirst'], DefaultSet].flatten.each do |r|
|
|
426
|
+
# Try MTA module candidates
|
|
427
|
+
next if haveloaded[r]
|
|
428
|
+
require LhostTable[r]
|
|
429
|
+
havesifted = Module.const_get(r).inquire(mailheader, bodystring)
|
|
430
|
+
haveloaded[r] = true
|
|
431
|
+
modulename = r
|
|
432
|
+
throw :PARSER if havesifted
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
unless haveloaded['Sisimai::RFC3464']
|
|
436
|
+
# When the all of Sisimai::Lhost::* modules did not return bounce data, call Sisimai::RFC3464;
|
|
437
|
+
require 'sisimai/rfc3464'
|
|
438
|
+
havesifted = Sisimai::RFC3464.inquire(mailheader, bodystring)
|
|
439
|
+
modulename = 'RFC3464'
|
|
440
|
+
throw :PARSER if havesifted
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
unless haveloaded['Sisimai::ARF']
|
|
444
|
+
# Feedback Loop message
|
|
445
|
+
require 'sisimai/arf'
|
|
446
|
+
havesifted = Sisimai::ARF.inquire(mailheader, bodystring) if Sisimai::ARF.is_arf(mailheader)
|
|
447
|
+
throw :PARSER if havesifted
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
unless haveloaded['Sisimai::RFC3834']
|
|
451
|
+
# Try to sift the message as auto reply message defined in RFC3834
|
|
452
|
+
require 'sisimai/rfc3834'
|
|
453
|
+
havesifted = Sisimai::RFC3834.inquire(mailheader, bodystring)
|
|
454
|
+
modulename = 'RFC3834'
|
|
455
|
+
throw :PARSER if havesifted
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
break # as of now, we have no sample email for coding this block
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
return nil unless havesifted
|
|
462
|
+
|
|
463
|
+
havesifted['catch'] = havecaught
|
|
464
|
+
modulename = modulename.sub(/\A.+::/, '')
|
|
465
|
+
havesifted['ds'].each do |e|
|
|
466
|
+
e['agent'] = modulename unless e['agent']
|
|
467
|
+
e.each_key { |a| e[a] ||= '' } # Replace nil with ""
|
|
468
|
+
end
|
|
469
|
+
return havesifted
|
|
347
470
|
end
|
|
348
|
-
return parseddata
|
|
349
|
-
end
|
|
350
471
|
|
|
472
|
+
end
|
|
351
473
|
end
|
|
352
474
|
end
|
|
353
475
|
|