sisimai 4.25.16-java → 5.0.2-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.
Files changed (180) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rake-test.yml +55 -0
  3. data/.travis.yml +3 -3
  4. data/ANALYTICAL-PRECISION +2 -2
  5. data/Benchmarks.mk +3 -3
  6. data/CONTRIBUTING +1 -1
  7. data/ChangeLog.md +451 -393
  8. data/Developers.mk +5 -6
  9. data/Gemfile +1 -1
  10. data/Makefile +15 -15
  11. data/README-JA.md +323 -149
  12. data/README.md +319 -149
  13. data/Rakefile +9 -3
  14. data/Repository.mk +2 -3
  15. data/lib/sisimai/address.rb +118 -74
  16. data/lib/sisimai/arf.rb +84 -82
  17. data/lib/sisimai/datetime.rb +5 -52
  18. data/lib/sisimai/{data → fact}/json.rb +7 -9
  19. data/lib/sisimai/fact/yaml.rb +31 -0
  20. data/lib/sisimai/fact.rb +506 -0
  21. data/lib/sisimai/lhost/activehunter.rb +12 -14
  22. data/lib/sisimai/lhost/amavis.rb +11 -14
  23. data/lib/sisimai/lhost/amazonses.rb +37 -42
  24. data/lib/sisimai/lhost/amazonworkmail.rb +15 -19
  25. data/lib/sisimai/lhost/aol.rb +12 -15
  26. data/lib/sisimai/lhost/apachejames.rb +19 -21
  27. data/lib/sisimai/lhost/barracuda.rb +10 -12
  28. data/lib/sisimai/lhost/bigfoot.rb +21 -22
  29. data/lib/sisimai/lhost/biglobe.rb +15 -16
  30. data/lib/sisimai/lhost/courier.rb +20 -20
  31. data/lib/sisimai/lhost/domino.rb +23 -20
  32. data/lib/sisimai/lhost/einsundeins.rb +23 -18
  33. data/lib/sisimai/lhost/exchange2003.rb +30 -29
  34. data/lib/sisimai/lhost/exchange2007.rb +70 -58
  35. data/lib/sisimai/lhost/exim.rb +179 -174
  36. data/lib/sisimai/lhost/ezweb.rb +31 -56
  37. data/lib/sisimai/lhost/facebook.rb +21 -34
  38. data/lib/sisimai/lhost/fml.rb +43 -48
  39. data/lib/sisimai/lhost/gmail.rb +29 -29
  40. data/lib/sisimai/lhost/gmx.rb +18 -17
  41. data/lib/sisimai/lhost/googlegroups.rb +11 -11
  42. data/lib/sisimai/lhost/gsuite.rb +21 -28
  43. data/lib/sisimai/lhost/imailserver.rb +25 -39
  44. data/lib/sisimai/lhost/interscanmss.rb +28 -31
  45. data/lib/sisimai/lhost/kddi.rb +22 -28
  46. data/lib/sisimai/lhost/mailfoundry.rb +11 -12
  47. data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
  48. data/lib/sisimai/lhost/mailru.rb +37 -40
  49. data/lib/sisimai/lhost/mcafee.rb +21 -31
  50. data/lib/sisimai/lhost/messagelabs.rb +17 -21
  51. data/lib/sisimai/lhost/messagingserver.rb +40 -37
  52. data/lib/sisimai/lhost/mfilter.rb +16 -17
  53. data/lib/sisimai/lhost/mxlogic.rb +24 -33
  54. data/lib/sisimai/lhost/notes.rb +17 -17
  55. data/lib/sisimai/lhost/office365.rb +64 -28
  56. data/lib/sisimai/lhost/opensmtpd.rb +12 -13
  57. data/lib/sisimai/lhost/outlook.rb +12 -16
  58. data/lib/sisimai/lhost/postfix.rb +179 -130
  59. data/lib/sisimai/lhost/powermta.rb +12 -14
  60. data/lib/sisimai/lhost/qmail.rb +44 -47
  61. data/lib/sisimai/lhost/receivingses.rb +15 -21
  62. data/lib/sisimai/lhost/sendgrid.rb +34 -34
  63. data/lib/sisimai/lhost/sendmail.rb +65 -53
  64. data/lib/sisimai/lhost/surfcontrol.rb +19 -19
  65. data/lib/sisimai/lhost/v5sendmail.rb +45 -39
  66. data/lib/sisimai/lhost/verizon.rb +35 -39
  67. data/lib/sisimai/lhost/x1.rb +18 -17
  68. data/lib/sisimai/lhost/x2.rb +17 -14
  69. data/lib/sisimai/lhost/x3.rb +19 -19
  70. data/lib/sisimai/lhost/x4.rb +72 -57
  71. data/lib/sisimai/lhost/x5.rb +17 -19
  72. data/lib/sisimai/lhost/x6.rb +41 -17
  73. data/lib/sisimai/lhost/yahoo.rb +17 -16
  74. data/lib/sisimai/lhost/yandex.rb +16 -21
  75. data/lib/sisimai/lhost/zoho.rb +16 -15
  76. data/lib/sisimai/lhost.rb +8 -10
  77. data/lib/sisimai/mail/maildir.rb +1 -3
  78. data/lib/sisimai/mail/mbox.rb +3 -4
  79. data/lib/sisimai/mail/memory.rb +0 -1
  80. data/lib/sisimai/mail/stdin.rb +1 -3
  81. data/lib/sisimai/mail.rb +3 -7
  82. data/lib/sisimai/mda.rb +28 -42
  83. data/lib/sisimai/message.rb +444 -326
  84. data/lib/sisimai/order.rb +5 -5
  85. data/lib/sisimai/reason/authfailure.rb +65 -0
  86. data/lib/sisimai/reason/badreputation.rb +53 -0
  87. data/lib/sisimai/reason/blocked.rb +96 -160
  88. data/lib/sisimai/reason/contenterror.rb +8 -9
  89. data/lib/sisimai/reason/delivered.rb +4 -6
  90. data/lib/sisimai/reason/exceedlimit.rb +10 -12
  91. data/lib/sisimai/reason/expired.rb +7 -8
  92. data/lib/sisimai/reason/feedback.rb +2 -3
  93. data/lib/sisimai/reason/filtered.rb +17 -19
  94. data/lib/sisimai/reason/hasmoved.rb +9 -10
  95. data/lib/sisimai/reason/hostunknown.rb +15 -15
  96. data/lib/sisimai/reason/mailboxfull.rb +11 -12
  97. data/lib/sisimai/reason/mailererror.rb +18 -20
  98. data/lib/sisimai/reason/mesgtoobig.rb +9 -11
  99. data/lib/sisimai/reason/networkerror.rb +5 -8
  100. data/lib/sisimai/reason/norelaying.rb +8 -11
  101. data/lib/sisimai/reason/notaccept.rb +13 -14
  102. data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
  103. data/lib/sisimai/reason/onhold.rb +6 -9
  104. data/lib/sisimai/reason/policyviolation.rb +14 -12
  105. data/lib/sisimai/reason/rejected.rb +26 -24
  106. data/lib/sisimai/reason/requireptr.rb +69 -0
  107. data/lib/sisimai/reason/securityerror.rb +34 -36
  108. data/lib/sisimai/reason/spamdetected.rb +115 -147
  109. data/lib/sisimai/reason/speeding.rb +49 -0
  110. data/lib/sisimai/reason/suspend.rb +12 -11
  111. data/lib/sisimai/reason/syntaxerror.rb +11 -10
  112. data/lib/sisimai/reason/systemerror.rb +7 -9
  113. data/lib/sisimai/reason/systemfull.rb +7 -8
  114. data/lib/sisimai/reason/toomanyconn.rb +9 -11
  115. data/lib/sisimai/reason/undefined.rb +2 -3
  116. data/lib/sisimai/reason/userunknown.rb +129 -146
  117. data/lib/sisimai/reason/vacation.rb +3 -4
  118. data/lib/sisimai/reason/virusdetected.rb +10 -11
  119. data/lib/sisimai/reason.rb +59 -64
  120. data/lib/sisimai/rfc1894.rb +55 -28
  121. data/lib/sisimai/rfc2045.rb +373 -0
  122. data/lib/sisimai/rfc3464.rb +250 -308
  123. data/lib/sisimai/rfc3834.rb +42 -45
  124. data/lib/sisimai/rfc5322.rb +177 -146
  125. data/lib/sisimai/rfc5965.rb +31 -0
  126. data/lib/sisimai/rhost/cox.rb +5 -6
  127. data/lib/sisimai/rhost/franceptt.rb +6 -8
  128. data/lib/sisimai/rhost/godaddy.rb +12 -12
  129. data/lib/sisimai/rhost/google.rb +530 -0
  130. data/lib/sisimai/rhost/iua.rb +9 -10
  131. data/lib/sisimai/rhost/kddi.rb +6 -8
  132. data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
  133. data/lib/sisimai/rhost/mimecast.rb +51 -42
  134. data/lib/sisimai/rhost/nttdocomo.rb +12 -12
  135. data/lib/sisimai/rhost/spectrum.rb +10 -12
  136. data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
  137. data/lib/sisimai/rhost.rb +23 -31
  138. data/lib/sisimai/smtp/command.rb +59 -0
  139. data/lib/sisimai/smtp/error.rb +4 -7
  140. data/lib/sisimai/smtp/reply.rb +161 -74
  141. data/lib/sisimai/smtp/status.rb +507 -393
  142. data/lib/sisimai/smtp/transcript.rb +124 -0
  143. data/lib/sisimai/smtp.rb +0 -1
  144. data/lib/sisimai/string.rb +74 -5
  145. data/lib/sisimai/time.rb +1 -2
  146. data/lib/sisimai/version.rb +1 -1
  147. data/lib/sisimai.rb +46 -31
  148. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
  149. data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
  150. data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
  151. data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
  152. data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
  153. data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
  154. data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
  155. data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
  156. data/set-of-emails/maildir/bsd/lhost-sendmail-60.eml +85 -0
  157. data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
  158. data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
  159. data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
  160. data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
  161. data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
  162. data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
  163. data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
  164. data/sisimai-java.gemspec +1 -1
  165. data/sisimai.gemspec +1 -1
  166. metadata +48 -26
  167. data/.rspec +0 -2
  168. data/lib/sisimai/data/yaml.rb +0 -33
  169. data/lib/sisimai/data.rb +0 -411
  170. data/lib/sisimai/mime.rb +0 -456
  171. data/lib/sisimai/rhost/googleapps.rb +0 -261
  172. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  178. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  179. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  180. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Qmail parses a bounce email which created by qmail.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Qmail parses a bounce email which created by qmail. Methods in the module are called
3
+ # from only Sisimai::Message.
4
4
  module Qmail
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/qmail.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^--- Below this line is a copy of the message[.]|.freeze
9
+ Boundaries = ['--- Below this line is a copy of the message.'].freeze
11
10
  StartingOf = {
12
11
  # qmail-remote.c:248| if (code >= 500) {
13
12
  # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
@@ -16,45 +15,33 @@ module Sisimai::Lhost
16
15
  #
17
16
  # Characters: K,Z,D in qmail-qmqpc.c, qmail-send.c, qmail-rspawn.c
18
17
  # K = success, Z = temporary error, D = permanent error
19
- message: ['Hi. This is the qmail'],
20
18
  error: ['Remote host said:'],
19
+ message: ['Hi. This is the qmail'],
20
+ rhost: ['Giving up on ', 'Connected to ', 'remote host '],
21
21
  }.freeze
22
-
23
- ReSMTP = {
22
+ CommandSet = {
24
23
  # Error text regular expressions which defined in qmail-remote.c
25
24
  # qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
26
- 'conn' => %r/(?:Error:)?Connected to [^ ]+ but greeting failed[.]/,
25
+ 'conn' => [' but greeting failed.'],
27
26
  # qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
28
- 'ehlo' => %r/(?:Error:)?Connected to [^ ]+ but my name was rejected[.]/,
27
+ 'ehlo' => [' but my name was rejected.'],
29
28
  # qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
30
29
  # reason = rejected
31
- 'mail' => %r/(?:Error:)?Connected to [^ ]+ but sender was rejected[.]/,
30
+ 'mail' => [' but sender was rejected.'],
32
31
  # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
33
32
  # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
34
33
  # reason = userunknown
35
- 'rcpt' => %r/(?:Error:)?[^ ]+ does not like recipient[.]/,
34
+ 'rcpt' => [' does not like recipient.'],
36
35
  # qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
37
36
  # qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
38
37
  # qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
39
38
  # qmail-remote.c:272| if (code >= 400) quit("Z"," failed after I sent the message");
40
- 'data' => %r{(?:
41
- (?:Error:)?[^ ]+[ ]failed[ ]on[ ]DATA[ ]command[.]
42
- |(?:Error:)?[^ ]+[ ]failed[ ]after[ ]I[ ]sent[ ]the[ ]message[.]
43
- )
44
- }x,
39
+ 'data' => [' failed on DATA command', ' failed after I sent the message'],
45
40
  }.freeze
46
- # qmail-remote.c:261| if (!flagbother) quit("DGiving up on ","");
47
- ReHost = %r{(?:
48
- Giving[ ]up[ ]on[ ]([^ ]+[0-9a-zA-Z])[.]?\z
49
- |Connected[ ]to[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]
50
- |remote[ ]host[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]said:
51
- )
52
- }x
53
41
 
54
42
  # qmail-send.c:922| ... (&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
55
43
  HasExpired = 'this message has been in the queue too long.'
56
- ReCommands = %r/Sorry, no SMTP connection got far enough; most progress was ([A-Z]{4}) /
57
- ReIsOnHold = %r/\A[^ ]+ does not like recipient[.][ ]+.+this message has been in the queue too long[.]\z/
44
+ OnHoldPair = [' does not like recipient.', 'this message has been in the queue too long.'].freeze
58
45
  FailOnLDAP = {
59
46
  # qmail-ldap-1.03-20040101.patch:19817 - 19866
60
47
  'suspend' => ['Mailaddress is administrative?le?y disabled'], # 5.2.1
@@ -104,27 +91,31 @@ module Sisimai::Lhost
104
91
  # @param [String] mbody Message body of a bounce email
105
92
  # @return [Hash] Bounce data list and message/rfc822 part
106
93
  # @return [Nil] it failed to parse or the arguments are missing
107
- def make(mhead, mbody)
94
+ def inquire(mhead, mbody)
108
95
  # Pre process email headers and the body part of the message which generated
109
96
  # by qmail, see https://cr.yp.to/qmail.html
110
97
  # e.g.) Received: (qmail 12345 invoked for bounce); 29 Apr 2009 12:34:56 -0000
111
98
  # Subject: failure notice
112
- tryto = /\A[(]qmail[ ]+\d+[ ]+invoked[ ]+(?:for[ ]+bounce|from[ ]+network)[)]/
99
+ tryto = [['(qmail ', 'invoked for bounce)'], ['(qmail ', 'invoked from ', 'network)']]
113
100
  match = 0
114
101
  match += 1 if mhead['subject'] == 'failure notice'
115
- match += 1 if mhead['received'].any? { |a| a =~ tryto }
102
+ mhead['received'].each do |e|
103
+ # Received: (qmail 2222 invoked for bounce);29 Apr 2017 23:34:45 +0900
104
+ # Received: (qmail 2202 invoked from network); 29 Apr 2018 00:00:00 +0900
105
+ match += 1 if tryto.any? { |a| Sisimai::String.aligned(e, a) }
106
+ end
116
107
  return nil unless match > 0
117
108
 
118
109
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
119
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
120
- bodyslices = emailsteak[0].split("\n")
110
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
111
+ bodyslices = emailparts[0].split("\n")
121
112
  readcursor = 0 # (Integer) Points the current cursor position
122
113
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
123
114
  v = nil
124
115
 
125
116
  while e = bodyslices.shift do
126
- # Read error messages and delivery status lines from the head of the email
127
- # to the previous line of the beginning of the original message.
117
+ # Read error messages and delivery status lines from the head of the email to the previous
118
+ # line of the beginning of the original message.
128
119
  if readcursor == 0
129
120
  # Beginning of the bounce message or delivery status part
130
121
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -139,14 +130,14 @@ module Sisimai::Lhost
139
130
  # Giving up on 192.0.2.153.
140
131
  v = dscontents[-1]
141
132
 
142
- if cv = e.match(/\A(?:To[ ]*:)?[<](.+[@].+)[>]:[ \t]*\z/)
133
+ if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>', ':'])
143
134
  # <kijitora@example.jp>:
144
135
  if v['recipient']
145
136
  # There are multiple recipient addresses in the message body.
146
137
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
147
138
  v = dscontents[-1]
148
139
  end
149
- v['recipient'] = cv[1]
140
+ v['recipient'] = Sisimai::Address.s3s4(e[e.index('<'), e.size])
150
141
  recipients += 1
151
142
 
152
143
  elsif dscontents.size == recipients
@@ -157,8 +148,15 @@ module Sisimai::Lhost
157
148
  v['alterrors'] = e if e.start_with?(StartingOf[:error][0])
158
149
 
159
150
  next if v['rhost']
160
- next unless cv = e.match(ReHost)
161
- v['rhost'] = cv[1]
151
+ StartingOf[:rhost].each do |r|
152
+ # Find a remote host name
153
+ p1 = e.index(r); next unless p1
154
+ cm = r.size
155
+ p2 = e.index(' ', p1 + cm + 1) || p2 = e.rindex('.')
156
+
157
+ v['rhost'] = e[p1 + cm, p2 - p1 - cm]
158
+ break
159
+ end
162
160
  end
163
161
  end
164
162
  return nil unless recipients > 0
@@ -169,17 +167,16 @@ module Sisimai::Lhost
169
167
 
170
168
  unless e['command']
171
169
  # Get the SMTP command name for the session
172
- ReSMTP.each_key do |r|
170
+ CommandSet.each_key do |r|
173
171
  # Verify each regular expression of SMTP commands
174
- next unless e['diagnosis'] =~ ReSMTP[r]
172
+ next unless CommandSet[r].any? { |a| e['diagnosis'].include?(a) }
175
173
  e['command'] = r.upcase
176
174
  break
177
175
  end
178
176
 
179
- unless e['command']
180
- # Verify each regular expression of patches
181
- if cv = e['diagnosis'].match(ReCommands) then e['command'] = cv[1].upcase end
182
- e['command'] ||= ''
177
+ if e['diagnosis'].include?('Sorry, no SMTP connection got far enough; most progress was ')
178
+ # Get the last SMTP command:from the error message
179
+ e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis']) || ''
183
180
  end
184
181
  end
185
182
 
@@ -193,9 +190,8 @@ module Sisimai::Lhost
193
190
  e['reason'] = 'blocked'
194
191
  else
195
192
  # Try to match with each error message in the table
196
- if e['diagnosis'] =~ ReIsOnHold
197
- # To decide the reason require pattern match with
198
- # Sisimai::Reason::* modules
193
+ if Sisimai::String.aligned(e['diagnosis'], OnHoldPair)
194
+ # To decide the reason require pattern match with Sisimai::Reason::* modules
199
195
  e['reason'] = 'onhold'
200
196
  else
201
197
  MessagesOf.each_key do |r|
@@ -227,10 +223,11 @@ module Sisimai::Lhost
227
223
  end
228
224
  end
229
225
 
230
- e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
226
+ e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis'])
227
+ e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
231
228
  end
232
229
 
233
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
230
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
234
231
  end
235
232
  def description; return 'qmail'; end
236
233
  end
@@ -1,15 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::ReceivingSES parses a bounce email which created
3
- # by Amazon Simple Email Service. Methods in the module are called from
4
- # only Sisimai::Message.
2
+ # Sisimai::Lhost::ReceivingSES parses a bounce email which created by Amazon Simple Email Service.
3
+ # Methods in the module are called from only Sisimai::Message.
5
4
  module ReceivingSES
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/ReceivingSES.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
8
  # https://aws.amazon.com/ses/
11
9
  Indicators = Sisimai::Lhost.INDICATORS
12
- ReBackbone = %r|^content-type:[ ]text/rfc822-headers|.freeze
10
+ Boundaries = ['Content-Type: text/rfc822-headers'].freeze
13
11
  StartingOf = { message: ['This message could not be delivered.'] }.freeze
14
12
  MessagesOf = {
15
13
  # The followings are error messages in Rule sets/*/Actions/Template
@@ -24,26 +22,25 @@ module Sisimai::Lhost
24
22
  # @param [String] mbody Message body of a bounce email
25
23
  # @return [Hash] Bounce data list and message/rfc822 part
26
24
  # @return [Nil] it failed to parse or the arguments are missing
27
- def make(mhead, mbody)
25
+ def inquire(mhead, mbody)
28
26
  # X-SES-Outgoing: 2015.10.01-54.240.27.7
29
27
  # Feedback-ID: 1.us-west-2.HX6/J9OVlHTadQhEu1+wdF9DBj6n6Pa9sW5Y/0pSOi8=:AmazonSES
30
28
  return nil unless mhead['x-ses-outgoing']
31
29
 
32
- require 'sisimai/rfc1894'
33
30
  fieldtable = Sisimai::RFC1894.FIELDTABLE
34
31
  permessage = {} # (Hash) Store values of each Per-Message field
35
32
 
36
33
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
37
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
38
- bodyslices = emailsteak[0].split("\n")
34
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
35
+ bodyslices = emailparts[0].split("\n")
39
36
  readslices = ['']
40
37
  readcursor = 0 # (Integer) Points the current cursor position
41
38
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
42
39
  v = nil
43
40
 
44
41
  while e = bodyslices.shift do
45
- # Read error messages and delivery status lines from the head of the email
46
- # to the previous line of the beginning of the original message.
42
+ # Read error messages and delivery status lines from the head of the email to the previous
43
+ # line of the beginning of the original message.
47
44
  readslices << e # Save the current line for the next loop
48
45
 
49
46
  if readcursor == 0
@@ -84,14 +81,14 @@ module Sisimai::Lhost
84
81
  next unless fieldtable[o[0]]
85
82
  v[fieldtable[o[0]]] = o[2]
86
83
 
87
- next unless f == 1
84
+ next unless f
88
85
  permessage[fieldtable[o[0]]] = o[2]
89
86
  end
90
87
  else
91
88
  # Continued line of the value of Diagnostic-Code field
92
89
  next unless readslices[-2].start_with?('Diagnostic-Code:')
93
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
94
- v['diagnosis'] << ' ' << cv[1]
90
+ next unless e.start_with?(' ')
91
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
95
92
  readslices[-1] = 'Diagnostic-Code: ' << e
96
93
  end
97
94
  end
@@ -99,18 +96,15 @@ module Sisimai::Lhost
99
96
 
100
97
  dscontents.each do |e|
101
98
  # Set default values if each value is empty.
102
- e['lhost'] ||= permessage['rhost']
103
99
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
104
100
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].tr("\n", ' '))
105
101
 
106
102
  if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
107
103
  # Get other D.S.N. value from the error message
108
104
  errormessage = e['diagnosis']
109
-
110
- if cv = e['diagnosis'].match(/["'](\d[.]\d[.]\d.+)['"]/)
111
- # 5.1.0 - Unknown address error 550-'5.7.1 ...
112
- errormessage = cv[1]
113
- end
105
+ p1 = e['diagnosis'].index("-'"); p1 = e['diagnosis'].index('-"') unless p1
106
+ p2 = e['diagnosis'].rindex("' "); p2 = e['diagnosis'].rindex('" ') unless p2
107
+ errormessage = e['diagnosis'][p1 + 2, p2 - p1 - 2] if p1 && p2
114
108
  e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
115
109
  end
116
110
 
@@ -123,7 +117,7 @@ module Sisimai::Lhost
123
117
  e['reason'] ||= Sisimai::SMTP::Status.name(e['status']) || ''
124
118
  end
125
119
 
126
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
120
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
127
121
  end
128
122
  def description; return 'Amazon SES(Receiving): https://aws.amazon.com/ses/'; end
129
123
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::SendGrid parses a bounce email which created by
3
- # SendGrid. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::SendGrid parses a bounce email which created by SendGrid. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module SendGrid
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/SendGrid.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
11
10
  StartingOf = { message: ['This is an automatically generated message from SendGrid.'] }.freeze
12
11
 
13
12
  # Parse bounce messages from SendGrid
@@ -15,29 +14,29 @@ module Sisimai::Lhost
15
14
  # @param [String] mbody Message body of a bounce email
16
15
  # @return [Hash] Bounce data list and message/rfc822 part
17
16
  # @return [Nil] it failed to parse or the arguments are missing
18
- def make(mhead, mbody)
17
+ def inquire(mhead, mbody)
19
18
  # Return-Path: <apps@sendgrid.net>
20
19
  # X-Mailer: MIME-tools 5.502 (Entity 5.502)
21
20
  return nil unless mhead['return-path']
22
21
  return nil unless mhead['return-path'] == '<apps@sendgrid.net>'
23
22
  return nil unless mhead['subject'] == 'Undelivered Mail Returned to Sender'
24
23
 
25
- require 'sisimai/rfc1894'
24
+ require 'sisimai/smtp/command'
26
25
  fieldtable = Sisimai::RFC1894.FIELDTABLE
27
26
  permessage = {} # (Hash) Store values of each Per-Message field
28
27
 
29
28
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
30
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
31
- bodyslices = emailsteak[0].split("\n")
29
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
30
+ bodyslices = emailparts[0].split("\n")
32
31
  readslices = ['']
33
32
  readcursor = 0 # (Integer) Points the current cursor position
34
33
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
35
- commandtxt = '' # (String) SMTP Command name begin with the string '>>>'
34
+ thecommand = '' # (String) SMTP Command name begin with the string '>>>'
36
35
  v = nil
37
36
 
38
37
  while e = bodyslices.shift do
39
- # Read error messages and delivery status lines from the head of the email
40
- # to the previous line of the beginning of the original message.
38
+ # Read error messages and delivery status lines from the head of the email to the previous
39
+ # line of the beginning of the original message.
41
40
  readslices << e # Save the current line for the next loop
42
41
 
43
42
  if readcursor == 0
@@ -57,8 +56,7 @@ module Sisimai::Lhost
57
56
  # Fallback code for empty value or invalid formatted value
58
57
  # - Status: (empty)
59
58
  # - Diagnostic-Code: 550 5.1.1 ... (No "diagnostic-type" sub field)
60
- next unless cv = e.match(/\ADiagnostic-Code:[ ]*(.+)/)
61
- v['diagnosis'] = cv[1]
59
+ v['diagnosis'] = e[e.index(':') + 2, e.size] if e.start_with?('Diagnostic-Code: ')
62
60
  next
63
61
  end
64
62
 
@@ -84,29 +82,37 @@ module Sisimai::Lhost
84
82
  v['diagnosis'] = o[2]
85
83
  elsif o[-1] == 'date'
86
84
  # Arrival-Date: 2012-12-31 23-59-59
87
- next unless cv = e.match(/\AArrival-Date: (\d{4})[-](\d{2})[-](\d{2}) (\d{2})[-](\d{2})[-](\d{2})\z/)
88
- o[1] << 'Thu, ' << cv[3] + ' '
89
- o[1] << Sisimai::DateTime.monthname(false)[cv[2].to_i - 1]
90
- o[1] << ' ' << cv[1] + ' ' << [cv[4], cv[5], cv[6]].join(':')
85
+ next unless e.start_with?('Arrival-Date: ')
86
+ cf = e[e.index(': ') + 2, e.size].split(' '); next unless cf.size == 2
87
+ cw = cf[0].split('-'); next unless cw.size == 3
88
+ ce = cf[1].split('-'); next unless ce.size == 3
89
+
90
+ o[1] << 'Thu, ' << cw[2] + ' '
91
+ o[1] << Sisimai::DateTime.monthname(false)[cw[1].to_i - 1]
92
+ o[1] << ' ' << cw[0] + ' ' << ce.join(':')
91
93
  o[1] << ' ' << Sisimai::DateTime.abbr2tz('CDT')
92
94
  else
93
95
  # Other DSN fields defined in RFC3464
94
96
  next unless fieldtable[o[0]]
95
97
  v[fieldtable[o[0]]] = o[2]
96
98
 
97
- next unless f == 1
99
+ next unless f
98
100
  permessage[fieldtable[o[0]]] = o[2]
99
101
  end
100
102
  else
101
103
  # The line does not begin with a DSN field defined in RFC3464
102
- if cv = e.match(/.+ in (?:End of )?([A-Z]{4}).*\z/)
104
+ if cv = Sisimai::SMTP::Command.find(e)
103
105
  # in RCPT TO, in MAIL FROM, end of DATA
104
- commandtxt = cv[1]
106
+ thecommand = cv
107
+ elsif e.start_with?('Diagnostic-Code: ')
108
+ # Diagnostic-Code: 550 5.1.1 <kijitora@example.jp>... User Unknown
109
+ v['diagnosis'] = e[e.index(':') + 2, e.size]
105
110
  else
106
111
  # Continued line of the value of Diagnostic-Code field
107
112
  next unless readslices[-2].start_with?('Diagnostic-Code:')
108
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
109
- v['diagnosis'] << ' ' << cv[1]
113
+ next unless e.start_with?(' ')
114
+ v['diagnosis'] ||= ''
115
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
110
116
  readslices[-1] = 'Diagnostic-Code: ' << e
111
117
  end
112
118
  end
@@ -116,14 +122,12 @@ module Sisimai::Lhost
116
122
  dscontents.each do |e|
117
123
  # Get the value of SMTP status code as a pseudo D.S.N.
118
124
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
119
- if cv = e['diagnosis'].match(/\b([45])\d\d[ \t]*/)
120
- # 4xx or 5xx
121
- e['status'] = cv[1] + '.0.0'
122
- end
125
+ e['replycode'] = Sisimai::SMTP::Reply.find(e['diagnosis']) || ''
126
+ e['status'] = e['replycode'][0, 1] + '.0.0' if e['replycode'].size == 3
127
+ e['command'] = thecommand
123
128
 
124
129
  if e['status'] == '5.0.0' || e['status'] == '4.0.0'
125
- # Get the value of D.S.N. from the error message or the value of
126
- # Diagnostic-Code header.
130
+ # Get the value of D.S.N. from the error message or the value of Diagnostic-Code header.
127
131
  e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || e['status']
128
132
  end
129
133
 
@@ -131,17 +135,13 @@ module Sisimai::Lhost
131
135
  # Action: expired
132
136
  e['reason'] = 'expired'
133
137
  if !e['status'] || e['status'].end_with?('.0.0')
134
- # Set pseudo Status code value if the value of Status is not
135
- # defined or 4.0.0 or 5.0.0.
138
+ # Set pseudo Status code value if the value of Status is not defined or 4.0.0 or 5.0.0.
136
139
  e['status'] = Sisimai::SMTP::Status.code('expired') || e['status']
137
140
  end
138
141
  end
139
-
140
- e['lhost'] ||= permessage['rhost']
141
- e['command'] = commandtxt
142
142
  end
143
143
 
144
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
144
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
145
145
  end
146
146
  def description; return 'SendGrid: https://sendgrid.com/'; end
147
147
  end