sisimai 4.25.15-java → 5.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +3 -3
  3. data/ANALYTICAL-PRECISION +2 -2
  4. data/Benchmarks.mk +3 -3
  5. data/CONTRIBUTING +1 -1
  6. data/ChangeLog.md +419 -388
  7. data/Developers.mk +5 -6
  8. data/Gemfile +1 -1
  9. data/Makefile +15 -15
  10. data/README-JA.md +140 -78
  11. data/README.md +290 -143
  12. data/Rakefile +9 -3
  13. data/Repository.mk +2 -3
  14. data/lib/sisimai/address.rb +118 -74
  15. data/lib/sisimai/arf.rb +84 -82
  16. data/lib/sisimai/datetime.rb +5 -52
  17. data/lib/sisimai/{data → fact}/json.rb +7 -9
  18. data/lib/sisimai/fact/yaml.rb +31 -0
  19. data/lib/sisimai/fact.rb +468 -0
  20. data/lib/sisimai/lhost/activehunter.rb +12 -14
  21. data/lib/sisimai/lhost/amavis.rb +11 -14
  22. data/lib/sisimai/lhost/amazonses.rb +37 -41
  23. data/lib/sisimai/lhost/amazonworkmail.rb +15 -18
  24. data/lib/sisimai/lhost/aol.rb +12 -14
  25. data/lib/sisimai/lhost/apachejames.rb +19 -21
  26. data/lib/sisimai/lhost/barracuda.rb +10 -12
  27. data/lib/sisimai/lhost/bigfoot.rb +21 -21
  28. data/lib/sisimai/lhost/biglobe.rb +15 -16
  29. data/lib/sisimai/lhost/courier.rb +20 -20
  30. data/lib/sisimai/lhost/domino.rb +23 -19
  31. data/lib/sisimai/lhost/einsundeins.rb +23 -18
  32. data/lib/sisimai/lhost/exchange2003.rb +30 -29
  33. data/lib/sisimai/lhost/exchange2007.rb +70 -58
  34. data/lib/sisimai/lhost/exim.rb +175 -161
  35. data/lib/sisimai/lhost/ezweb.rb +31 -56
  36. data/lib/sisimai/lhost/facebook.rb +21 -33
  37. data/lib/sisimai/lhost/fml.rb +43 -48
  38. data/lib/sisimai/lhost/gmail.rb +29 -29
  39. data/lib/sisimai/lhost/gmx.rb +18 -17
  40. data/lib/sisimai/lhost/googlegroups.rb +9 -10
  41. data/lib/sisimai/lhost/gsuite.rb +21 -27
  42. data/lib/sisimai/lhost/imailserver.rb +25 -39
  43. data/lib/sisimai/lhost/interscanmss.rb +28 -31
  44. data/lib/sisimai/lhost/kddi.rb +22 -28
  45. data/lib/sisimai/lhost/mailfoundry.rb +11 -12
  46. data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
  47. data/lib/sisimai/lhost/mailru.rb +33 -27
  48. data/lib/sisimai/lhost/mcafee.rb +21 -31
  49. data/lib/sisimai/lhost/messagelabs.rb +17 -20
  50. data/lib/sisimai/lhost/messagingserver.rb +40 -37
  51. data/lib/sisimai/lhost/mfilter.rb +15 -16
  52. data/lib/sisimai/lhost/mxlogic.rb +24 -23
  53. data/lib/sisimai/lhost/notes.rb +17 -17
  54. data/lib/sisimai/lhost/office365.rb +63 -27
  55. data/lib/sisimai/lhost/opensmtpd.rb +12 -13
  56. data/lib/sisimai/lhost/outlook.rb +12 -15
  57. data/lib/sisimai/lhost/postfix.rb +179 -129
  58. data/lib/sisimai/lhost/powermta.rb +12 -14
  59. data/lib/sisimai/lhost/qmail.rb +44 -47
  60. data/lib/sisimai/lhost/receivingses.rb +15 -20
  61. data/lib/sisimai/lhost/sendgrid.rb +34 -32
  62. data/lib/sisimai/lhost/sendmail.rb +66 -53
  63. data/lib/sisimai/lhost/surfcontrol.rb +19 -19
  64. data/lib/sisimai/lhost/v5sendmail.rb +45 -39
  65. data/lib/sisimai/lhost/verizon.rb +35 -39
  66. data/lib/sisimai/lhost/x1.rb +18 -17
  67. data/lib/sisimai/lhost/x2.rb +17 -14
  68. data/lib/sisimai/lhost/x3.rb +19 -19
  69. data/lib/sisimai/lhost/x4.rb +72 -57
  70. data/lib/sisimai/lhost/x5.rb +17 -19
  71. data/lib/sisimai/lhost/x6.rb +41 -17
  72. data/lib/sisimai/lhost/yahoo.rb +17 -16
  73. data/lib/sisimai/lhost/yandex.rb +16 -20
  74. data/lib/sisimai/lhost/zoho.rb +16 -15
  75. data/lib/sisimai/lhost.rb +8 -10
  76. data/lib/sisimai/mail/maildir.rb +1 -3
  77. data/lib/sisimai/mail/mbox.rb +3 -4
  78. data/lib/sisimai/mail/memory.rb +0 -1
  79. data/lib/sisimai/mail/stdin.rb +1 -3
  80. data/lib/sisimai/mail.rb +3 -7
  81. data/lib/sisimai/mda.rb +28 -42
  82. data/lib/sisimai/message.rb +435 -313
  83. data/lib/sisimai/order.rb +5 -5
  84. data/lib/sisimai/reason/authfailure.rb +64 -0
  85. data/lib/sisimai/reason/badreputation.rb +53 -0
  86. data/lib/sisimai/reason/blocked.rb +94 -160
  87. data/lib/sisimai/reason/contenterror.rb +8 -9
  88. data/lib/sisimai/reason/delivered.rb +4 -6
  89. data/lib/sisimai/reason/exceedlimit.rb +10 -12
  90. data/lib/sisimai/reason/expired.rb +6 -8
  91. data/lib/sisimai/reason/feedback.rb +2 -3
  92. data/lib/sisimai/reason/filtered.rb +17 -19
  93. data/lib/sisimai/reason/hasmoved.rb +9 -10
  94. data/lib/sisimai/reason/hostunknown.rb +15 -15
  95. data/lib/sisimai/reason/mailboxfull.rb +10 -12
  96. data/lib/sisimai/reason/mailererror.rb +18 -20
  97. data/lib/sisimai/reason/mesgtoobig.rb +9 -11
  98. data/lib/sisimai/reason/networkerror.rb +5 -8
  99. data/lib/sisimai/reason/norelaying.rb +8 -11
  100. data/lib/sisimai/reason/notaccept.rb +13 -14
  101. data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
  102. data/lib/sisimai/reason/onhold.rb +6 -9
  103. data/lib/sisimai/reason/policyviolation.rb +14 -12
  104. data/lib/sisimai/reason/rejected.rb +26 -24
  105. data/lib/sisimai/reason/requireptr.rb +69 -0
  106. data/lib/sisimai/reason/securityerror.rb +33 -36
  107. data/lib/sisimai/reason/spamdetected.rb +114 -147
  108. data/lib/sisimai/reason/speeding.rb +49 -0
  109. data/lib/sisimai/reason/suspend.rb +11 -11
  110. data/lib/sisimai/reason/syntaxerror.rb +11 -10
  111. data/lib/sisimai/reason/systemerror.rb +7 -9
  112. data/lib/sisimai/reason/systemfull.rb +7 -8
  113. data/lib/sisimai/reason/toomanyconn.rb +9 -11
  114. data/lib/sisimai/reason/undefined.rb +2 -3
  115. data/lib/sisimai/reason/userunknown.rb +129 -146
  116. data/lib/sisimai/reason/vacation.rb +3 -4
  117. data/lib/sisimai/reason/virusdetected.rb +10 -11
  118. data/lib/sisimai/reason.rb +59 -64
  119. data/lib/sisimai/rfc1894.rb +55 -28
  120. data/lib/sisimai/rfc2045.rb +373 -0
  121. data/lib/sisimai/rfc3464.rb +250 -308
  122. data/lib/sisimai/rfc3834.rb +42 -47
  123. data/lib/sisimai/rfc5322.rb +75 -100
  124. data/lib/sisimai/rfc5965.rb +31 -0
  125. data/lib/sisimai/rhost/cox.rb +5 -6
  126. data/lib/sisimai/rhost/franceptt.rb +6 -8
  127. data/lib/sisimai/rhost/godaddy.rb +12 -12
  128. data/lib/sisimai/rhost/{googleapps.rb → google.rb} +80 -72
  129. data/lib/sisimai/rhost/iua.rb +9 -10
  130. data/lib/sisimai/rhost/kddi.rb +6 -8
  131. data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
  132. data/lib/sisimai/rhost/mimecast.rb +42 -40
  133. data/lib/sisimai/rhost/nttdocomo.rb +12 -12
  134. data/lib/sisimai/rhost/spectrum.rb +10 -12
  135. data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
  136. data/lib/sisimai/rhost.rb +23 -31
  137. data/lib/sisimai/smtp/command.rb +59 -0
  138. data/lib/sisimai/smtp/error.rb +4 -7
  139. data/lib/sisimai/smtp/reply.rb +161 -74
  140. data/lib/sisimai/smtp/status.rb +504 -393
  141. data/lib/sisimai/smtp/transcript.rb +124 -0
  142. data/lib/sisimai/smtp.rb +0 -1
  143. data/lib/sisimai/string.rb +74 -5
  144. data/lib/sisimai/time.rb +1 -2
  145. data/lib/sisimai/version.rb +1 -1
  146. data/lib/sisimai.rb +35 -21
  147. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
  148. data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
  149. data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
  150. data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
  151. data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
  152. data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
  153. data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
  154. data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
  155. data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
  156. data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
  157. data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
  158. data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
  159. data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
  160. data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
  161. data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
  162. data/sisimai-java.gemspec +1 -1
  163. data/sisimai.gemspec +1 -1
  164. metadata +42 -22
  165. data/.rspec +0 -2
  166. data/lib/sisimai/data/yaml.rb +0 -33
  167. data/lib/sisimai/data.rb +0 -411
  168. data/lib/sisimai/mime.rb +0 -456
  169. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  170. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  177. /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::X3 parses a bounce email which created by Unknown MTA #3.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::X3 parses a bounce email which created by Unknown MTA #3. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module X3
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/X3.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 Delivery Status Notification.'] }.freeze
12
11
 
13
12
  # Parse bounce messages from Unknown MTA #3
@@ -15,20 +14,21 @@ 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 nil unless mhead['subject'].start_with?('Delivery status notification')
20
19
  return nil unless mhead['from'].start_with?('Mail Delivery System')
21
20
 
21
+ require 'sisimai/smtp/command'
22
22
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
23
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
24
- bodyslices = emailsteak[0].split("\n")
23
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
24
+ bodyslices = emailparts[0].split("\n")
25
25
  readcursor = 0 # (Integer) Points the current cursor position
26
26
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
27
27
  v = nil
28
28
 
29
29
  while e = bodyslices.shift do
30
- # Read error messages and delivery status lines from the head of the email
31
- # to the previous line of the beginning of the original message.
30
+ # Read error messages and delivery status lines from the head of the email to the previous
31
+ # line of the beginning of the original message.
32
32
  if readcursor == 0
33
33
  # Beginning of the bounce message or delivery status part
34
34
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -54,29 +54,29 @@ module Sisimai::Lhost
54
54
  # ============================================================================
55
55
  v = dscontents[-1]
56
56
 
57
- if cv = e.match(/\A[ \t]+[*][ ]([^ ]+[@][^ ]+)\z/)
57
+ if e.include?(' * ') && e.include?('@')
58
58
  # * kijitora@example.com
59
59
  if v['recipient']
60
60
  # There are multiple recipient addresses in the message body.
61
61
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
62
62
  v = dscontents[-1]
63
63
  end
64
- v['recipient'] = cv[1]
64
+ v['recipient'] = e[e.index(' * ') + 3, e.size]
65
65
  recipients += 1
66
66
  else
67
67
  # Detect error message
68
- if cv = e.match(/\ASMTP:([^ ]+)[ ](.+)\z/)
68
+ if e.start_with?('SMTP:')
69
69
  # SMTP:RCPT host 192.0.2.8: 553 5.3.0 <kijitora@example.com>... No such user here
70
- v['command'] = cv[1].upcase
71
- v['diagnosis'] = cv[2]
70
+ v['command'] = Sisimai::SMTP::Command.find(e)
71
+ v['diagnosis'] = e
72
72
 
73
- elsif cv = e.match(/\ARouting: (.+)/)
73
+ elsif e.start_with?('Routing: ')
74
74
  # Routing: Could not find a gateway for kijitora@example.co.jp
75
- v['diagnosis'] = cv[1]
75
+ v['diagnosis'] = e[9, e.size]
76
76
 
77
- elsif cv = e.match(/\ADiagnostic-Code: smtp; (.+)/)
77
+ elsif e.start_with?('Diagnostic-Code: smtp; ')
78
78
  # Diagnostic-Code: smtp; 552 5.2.2 Over quota
79
- v['diagnosis'] = cv[1]
79
+ v['diagnosis'] = e[e.index(';') + 2, e.size]
80
80
  end
81
81
  end
82
82
  end
@@ -87,7 +87,7 @@ module Sisimai::Lhost
87
87
  e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
88
88
  end
89
89
 
90
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
90
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
91
91
  end
92
92
  def description; return 'Unknown MTA #3'; end
93
93
  end
@@ -1,16 +1,14 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::X4 parses a bounce email which created by some qmail clone.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::X4 parses a bounce email which created by some qmail clone. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module X4
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/X4.pm
7
6
  # MTA module for qmail clones
8
7
  require 'sisimai/lhost'
9
8
 
10
9
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r/^---[ ](?:Below this line is a copy of the message|Original message follows)[.]/.freeze
12
- StartingOf = { error: ['Remote host said:'] }.freeze
13
- MarkingsOf = {
10
+ Boundaries = ['--- Below this line is a copy of the message.', 'Original message follows.'].freeze
11
+ StartingOf = {
14
12
  # qmail-remote.c:248| if (code >= 500) {
15
13
  # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
16
14
  # qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
@@ -18,24 +16,39 @@ module Sisimai::Lhost
18
16
  #
19
17
  # Characters: K,Z,D in qmail-qmqpc.c, qmail-send.c, qmail-rspawn.c
20
18
  # K = success, Z = temporary error, D = permanent error
21
- message: %r{\A(?>
22
- He/Her[ ]is[ ]not[ ].+[ ]user
23
- |Hi[.][ ].+[ ]unable[ ]to[ ]deliver[ ]your[ ]message[ ]to[ ]
24
- the[ ]following[ ]addresses
25
- |Su[ ]mensaje[ ]no[ ]pudo[ ]ser[ ]entregado
26
- |This[ ]is[ ]the[ ](?:
27
- machine[ ]generated[ ]message[ ]from[ ]mail[ ]service
28
- |mail[ ]delivery[ ]agent[ ]at
29
- )
30
- |Unable[ ]to[ ]deliver[ ]message[ ]to[ ]the[ ]following[ ]address
31
- |Unfortunately,[ ]your[ ]mail[ ]was[ ]not[ ]delivered[ ]to[ ]the[ ]following[ ]address:
32
- |Your[ ](?:
33
- mail[ ]message[ ]to[ ]the[ ]following[ ]address
34
- |message[ ]to[ ]the[ ]following[ ]addresses
35
- )
36
- |We're[ ]sorry[.]
37
- )
38
- }x,
19
+ error: ['Remote host said:'],
20
+ message: [
21
+ 'He/Her is not ',
22
+ 'unable to deliver your message to the following addresses',
23
+ 'Su mensaje no pudo ser entregado',
24
+ 'This is the machine generated message from mail service',
25
+ 'This is the mail delivery agent at',
26
+ 'Unable to deliver message to the following address',
27
+ 'Unfortunately, your mail was not delivered to the following address:',
28
+ 'Your mail message to the following address',
29
+ 'Your message to the following addresses',
30
+ "We're sorry.",
31
+ ],
32
+ rhost: ['Giving up on ', 'Connected to ', 'remote host '],
33
+ }.freeze
34
+ CommandSet = {
35
+ # Error text regular expressions which defined in qmail-remote.c
36
+ # qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
37
+ 'conn' => [' but greeting failed.'],
38
+ # qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
39
+ 'ehlo' => [' but my name was rejected.'],
40
+ # qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
41
+ # reason = rejected
42
+ 'mail' => [' but sender was rejected.'],
43
+ # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
44
+ # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
45
+ # reason = userunknown
46
+ 'rcpt' => [' does not like recipient.'],
47
+ # qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
48
+ # qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
49
+ # qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
50
+ # qmail-remote.c:272| if (code >= 400) quit("Z"," failed after I sent the message");
51
+ 'data' => [' failed on DATA command', ' failed after I sent the message'],
39
52
  }.freeze
40
53
 
41
54
  ReSMTP = {
@@ -61,18 +74,10 @@ module Sisimai::Lhost
61
74
  )
62
75
  }x,
63
76
  }.freeze
64
- # qmail-remote.c:261| if (!flagbother) quit("DGiving up on ","");
65
- ReHost = %r{(?:
66
- Giving[ ]up[ ]on[ ]([^ ]+[0-9a-zA-Z])[.]?\z
67
- |Connected[ ]to[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]
68
- |remote[ ]host[ ]([-0-9a-zA-Z.]+[0-9a-zA-Z])[ ]said:
69
- )
70
- }x
71
77
 
72
78
  # qmail-send.c:922| ... (&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
73
79
  HasExpired = 'this message has been in the queue too long.'
74
- ReIsOnHold = %r/\A[^ ]+ does not like recipient[.][ ]+.+this message has been in the queue too long[.]\z/
75
- ReCommands = %r/Sorry, no SMTP connection got far enough; most progress was ([A-Z]{4})[ ]/
80
+ OnHoldPair = [' does not like recipient.', 'this message has been in the queue too long.'].freeze
76
81
  FailOnLDAP = {
77
82
  # qmail-ldap-1.03-20040101.patch:19817 - 19866
78
83
  'suspend' => ['Mailaddress is administrative?le?y disabled'], # 5.2.1
@@ -122,30 +127,34 @@ module Sisimai::Lhost
122
127
  # @param [String] mbody Message body of a bounce email
123
128
  # @return [Hash] Bounce data list and message/rfc822 part
124
129
  # @return [Nil] it failed to parse or the arguments are missing
125
- def make(mhead, mbody)
126
- # Pre process email headers and the body part of the message which generated
127
- # by qmail, see https://cr.yp.to/qmail.html
130
+ def inquire(mhead, mbody)
131
+ # Pre process email headers and the body part of the message which generated by qmail, see
132
+ # https://cr.yp.to/qmail.html
128
133
  # e.g.) Received: (qmail 12345 invoked for bounce); 29 Apr 2009 12:34:56 -0000
129
134
  # Subject: failure notice
130
- tryto = %r/\A[(]qmail[ ]+\d+[ ]+invoked[ ]+for[ ]+bounce[)]/
135
+ tryto = [['(qmail ', 'invoked for bounce)'], ['(qmail ', 'invoked from ', 'network)']].freeze
131
136
  match = 0
132
137
  match += 1 if mhead['subject'].start_with?('failure notice', 'Permanent Delivery Failure')
133
- match += 1 if mhead['received'].any? { |a| a =~ tryto }
138
+ mhead['received'].each do |e|
139
+ # Received: (qmail 2222 invoked for bounce);29 Apr 2017 23:34:45 +0900
140
+ # Received: (qmail 2202 invoked from network); 29 Apr 2018 00:00:00 +0900
141
+ match += 1 if tryto.any? { |a| Sisimai::String.aligned(e, a) }
142
+ end
134
143
  return nil unless match > 0
135
144
 
136
145
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
137
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
138
- bodyslices = emailsteak[0].split("\n")
146
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
147
+ bodyslices = emailparts[0].split("\n")
139
148
  readcursor = 0 # (Integer) Points the current cursor position
140
149
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
141
150
  v = nil
142
151
 
143
152
  while e = bodyslices.shift do
144
- # Read error messages and delivery status lines from the head of the email
145
- # to the previous line of the beginning of the original message.
153
+ # Read error messages and delivery status lines from the head of the email to the previous
154
+ # line of the beginning of the original message.
146
155
  if readcursor == 0
147
156
  # Beginning of the bounce message or delivery status part
148
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
157
+ readcursor |= Indicators[:deliverystatus] if StartingOf[:message].any? { |a| e.include?(a) }
149
158
  next
150
159
  end
151
160
  next if (readcursor & Indicators[:deliverystatus]) == 0
@@ -157,14 +166,14 @@ module Sisimai::Lhost
157
166
  # Giving up on 192.0.2.153.
158
167
  v = dscontents[-1]
159
168
 
160
- if cv = e.match(/\A(?:To[ ]*:)?[<](.+[@].+)[>]:[ ]*\z/)
169
+ if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>', ':'])
161
170
  # <kijitora@example.jp>:
162
171
  if v['recipient']
163
172
  # There are multiple recipient addresses in the message body.
164
173
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
165
174
  v = dscontents[-1]
166
175
  end
167
- v['recipient'] = cv[1]
176
+ v['recipient'] = Sisimai::Address.s3s4(e[e.index('<'), e.size])
168
177
  recipients += 1
169
178
 
170
179
  elsif dscontents.size == recipients
@@ -175,8 +184,15 @@ module Sisimai::Lhost
175
184
  v['alterrors'] = e if e.start_with?(StartingOf[:error][0])
176
185
 
177
186
  next if v['rhost']
178
- next unless cv = e.match(ReHost)
179
- v['rhost'] = cv[1]
187
+ StartingOf[:rhost].each do |r|
188
+ # Find a remote host name
189
+ p1 = e.index(r); next unless p1
190
+ cm = r.size
191
+ p2 = e.index(' ', p1 + cm + 1) || p2 = e.rindex('.')
192
+
193
+ v['rhost'] = e[p1 + cm, p2 - p1 - cm]
194
+ break
195
+ end
180
196
  end
181
197
  end
182
198
  return nil unless recipients > 0
@@ -186,17 +202,16 @@ module Sisimai::Lhost
186
202
 
187
203
  unless e['command']
188
204
  # Get the SMTP command name for the session
189
- ReSMTP.each_key do |r|
205
+ CommandSet.each_key do |r|
190
206
  # Verify each regular expression of SMTP commands
191
- next unless e['diagnosis'] =~ ReSMTP[r]
207
+ next unless CommandSet[r].any? { |a| e['diagnosis'].include?(a) }
192
208
  e['command'] = r.upcase
193
209
  break
194
210
  end
195
211
 
196
- unless e['command']
197
- # Verify each regular expression of patches
198
- if cv = e['diagnosis'].match(ReCommands) then e['command'] = cv[1].upcase end
199
- e['command'] ||= ''
212
+ if e['diagnosis'].include?('Sorry, no SMTP connection got far enough; most progress was ')
213
+ # Get the last SMTP command:from the error message
214
+ e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis']) || ''
200
215
  end
201
216
  end
202
217
 
@@ -210,9 +225,8 @@ module Sisimai::Lhost
210
225
  e['reason'] = 'blocked'
211
226
  else
212
227
  # Try to match with each error message in the table
213
- if e['diagnosis'] =~ ReIsOnHold
214
- # To decide the reason require pattern match with
215
- # Sisimai::Reason::* modules
228
+ if Sisimai::String.aligned(e['diagnosis'], OnHoldPair)
229
+ # To decide the reason require pattern match with Sisimai::Reason::* modules
216
230
  e['reason'] = 'onhold'
217
231
  else
218
232
  MessagesOf.each_key do |r|
@@ -243,9 +257,10 @@ module Sisimai::Lhost
243
257
  end
244
258
  end
245
259
  end
260
+ e['command'] ||= Sisimai::SMTP::Command.find(e['diagnosis'])
246
261
  end
247
262
 
248
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
263
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
249
264
  end
250
265
  def description; return 'Unknown MTA #4'; end
251
266
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::X5 parses a bounce email which created by Unknown MTA #5.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::X5 parses a bounce email which created by Unknown MTA #5. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module X5
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/X5.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: ['Content-Type: message/delivery-status'] }.freeze
12
11
 
13
12
  # Parse bounce messages from Unknown MTA #5
@@ -15,7 +14,7 @@ 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
  match = 0
20
19
  match += 1 if mhead['to'].to_s.include?('NotificationRecipients')
21
20
  if mhead['from'].include?('TWFpbCBEZWxpdmVyeSBTdWJzeXN0ZW0')
@@ -23,20 +22,19 @@ module Sisimai::Lhost
23
22
  # Mail Delivery Subsystem
24
23
  mhead['from'].split(' ').each do |f|
25
24
  # Check each element of From: header
26
- next unless Sisimai::MIME.is_mimeencoded(f)
27
- match += 1 if Sisimai::MIME.mimedecode([f]).include?('Mail Delivery Subsystem')
25
+ next unless Sisimai::RFC2045.is_encoded(f)
26
+ match += 1 if Sisimai::RFC2045.decodeH([f]).include?('Mail Delivery Subsystem')
28
27
  break
29
28
  end
30
29
  end
31
30
 
32
- if Sisimai::MIME.is_mimeencoded(mhead['subject'])
31
+ if Sisimai::RFC2045.is_encoded(mhead['subject'])
33
32
  # Subject: =?iso-2022-jp?B?UmV0dXJuZWQgbWFpbDogVXNlciB1bmtub3du?=
34
- plain = Sisimai::MIME.mimedecode([mhead['subject']])
33
+ plain = Sisimai::RFC2045.decodeH([mhead['subject']])
35
34
  match += 1 if plain.include?('Mail Delivery Subsystem')
36
35
  end
37
36
  return nil if match < 2
38
37
 
39
- require 'sisimai/rfc1894'
40
38
  fieldtable = Sisimai::RFC1894.FIELDTABLE
41
39
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
42
40
  readslices = ['']
@@ -45,14 +43,14 @@ module Sisimai::Lhost
45
43
  v = nil
46
44
 
47
45
  # Pick the second message/rfc822 part because the format of email-x5-*.eml is nested structure
48
- prefillets = mbody.split(ReBackbone, 2)
49
- prefillets[1].sub!(/\A.+?\n\n/s, '')
50
- emailsteak = Sisimai::RFC5322.fillet(prefillets[1], ReBackbone)
51
- bodyslices = emailsteak[0].split("\n")
46
+ cutsbefore = mbody.split(Boundaries[0], 2)
47
+ cutsbefore[1] = cutsbefore[1][cutsbefore[1].index("\n\n") + 2, cutsbefore[1].size]
48
+ emailparts = Sisimai::RFC5322.part(cutsbefore[1], Boundaries)
49
+ bodyslices = emailparts[0].split("\n")
52
50
 
53
51
  while e = bodyslices.shift do
54
- # Read error messages and delivery status lines from the head of the email
55
- # to the previous line of the beginning of the original message.
52
+ # Read error messages and delivery status lines from the head of the email to the previous
53
+ # line of the beginning of the original message.
56
54
  readslices << e # Save the current line for the next loop
57
55
 
58
56
  if readcursor == 0
@@ -97,15 +95,15 @@ module Sisimai::Lhost
97
95
  else
98
96
  # Continued line of the value of Diagnostic-Code field
99
97
  next unless readslices[-2].start_with?('Diagnostic-Code:')
100
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
101
- v['diagnosis'] << ' ' << cv[1]
98
+ next unless e.start_with?(' ')
99
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
102
100
  readslices[-1] = 'Diagnostic-Code: ' << e
103
101
  end
104
102
  end
105
103
  return nil unless recipients > 0
106
104
 
107
105
  dscontents.each { |e| e['diagnosis'] ||= Sisimai::String.sweep(e['diagnosis']) }
108
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
106
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
109
107
  end
110
108
  def description; return 'Unknown MTA #5'; end
111
109
  end
@@ -1,14 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::X6 parses a bounce email which created by Unknown MTA #6.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::X6 parses a bounce email which created by Unknown MTA #6. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module X6
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/X6.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r/^The attachment contains the original mail headers.+$/.freeze
11
- MarkingsOf = { message: %r/\A\d+[ ]*error[(]s[)]:/ }.freeze
9
+ Boundaries = ['The attachment contains the original mail headers'].freeze
10
+ StartingOf = { message: ['We had trouble delivering your message. Full details follow:'] }.freeze
12
11
 
13
12
  # Parse bounce messages from Unknown MTA #6
14
13
  # @param [Hash] mhead Message headers of a bounce email
@@ -16,33 +15,41 @@ module Sisimai::Lhost
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
17
  # @since v4.25.6
19
- def make(mhead, mbody)
18
+ def inquire(mhead, mbody)
20
19
  return nil unless mhead['subject'].start_with?('There was an error sending your mail')
21
20
 
22
21
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
23
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
24
- bodyslices = emailsteak[0].split("\n")
22
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
23
+ bodyslices = emailparts[0].split("\n")
25
24
  readcursor = 0 # (Integer) Points the current cursor position
26
25
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
27
26
  v = nil
28
27
 
29
28
  while e = bodyslices.shift do
30
- # Read error messages and delivery status lines from the head of the email
31
- # to the previous line of the beginning of the original message.
29
+ # Read error messages and delivery status lines from the head of the email to the previous
30
+ # line of the beginning of the original message.
32
31
  if readcursor == 0
33
32
  # Beginning of the bounce message or delivery status part
34
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
33
+ readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
35
34
  next
36
35
  end
37
36
  next if (readcursor & Indicators[:deliverystatus]) == 0
38
37
  next if e.empty?
39
38
 
39
+ # We had trouble delivering your message. Full details follow:
40
+ #
41
+ # Subject: 'Nyaan'
42
+ # Date: 'Thu, 29 Apr 2012 23:34:45 +0000'
43
+ #
40
44
  # 1 error(s):
41
45
  #
42
46
  # SMTP Server <mta2.example.jp> rejected recipient <kijitora@examplejp>
43
47
  # (Error following RCPT command). It responded as follows: [550 5.1.1 User unknown]v = dscontents[-1]
44
- v = dscontents[-1]
45
- if cv = e.match(/<([^ @]+[@][^ @]+)>/) || e.match(/errors:[ ]*([^ ]+[@][^ ]+)/)
48
+ v = dscontents[-1]
49
+ p1 = e.index('The following recipients returned permanent errors: ')
50
+ p2 = e.index('SMTP Server <')
51
+
52
+ if p1 == 0 || p2 == 0
46
53
  # SMTP Server <mta2.example.jp> rejected recipient <kijitora@examplejp>
47
54
  # The following recipients returned permanent errors: neko@example.jp.
48
55
  if v['recipient']
@@ -50,22 +57,39 @@ module Sisimai::Lhost
50
57
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
51
58
  v = dscontents[-1]
52
59
  end
53
- v['recipient'] = Sisimai::Address.s3s4(cv[1])
60
+
61
+ if p1 == 0
62
+ # The following recipients returned permanent errors: neko@example.jp.
63
+ p1 = e.index('errors: ')
64
+ p2 = e.index(' ', p1 + 8)
65
+ v['recipient'] = Sisimai::Address.s3s4(e[p1 + 8, p2 - p1 - 8])
66
+
67
+ elsif p2 == 0
68
+ # SMTP Server <mta2.example.jp> rejected recipient <kijitora@examplejp>
69
+ p1 = e.rindex('<')
70
+ p2 = e.rindex('>')
71
+ v['recipient'] = Sisimai::Address.s3s4(e[p1, p2 - p1])
72
+
73
+ else
74
+ next
75
+ end
76
+
54
77
  v['diagnosis'] = e
55
78
  recipients += 1
56
79
  end
57
80
  end
58
81
  return nil unless recipients > 0
59
82
 
83
+ require 'sisimai/smtp/command'
60
84
  dscontents.each do |e|
61
- if cv = e['diagnosis'].match(/\b(HELO|EHLO|MAIL|RCPT|DATA)\b/)
85
+ if cv = Sisimai::SMTP::Command.find(e['diagnosis'])
62
86
  # ...(Error following RCPT command).
63
- e['command'] = cv[1]
87
+ e['command'] = cv
64
88
  end
65
89
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
66
90
  end
67
91
 
68
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
92
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
69
93
  end
70
94
  def description; return 'Unknown MTA #6'; end
71
95
  end
@@ -1,13 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Yahoo parses a bounce email which created by Yahoo! MAIL.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Yahoo parses a bounce email which created by Yahoo! MAIL. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module Yahoo
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Yahoo.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 = { message: ['Sorry, we were unable to deliver your message'] }.freeze
12
11
 
13
12
  # Parse bounce messages from Yahoo! MAIL
@@ -15,23 +14,24 @@ 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
  # X-YMailISG: YtyUVyYWLDsbDh...
20
19
  # X-YMail-JAS: Pb65aU4VM1mei...
21
20
  # X-YMail-OSG: bTIbpDEVM1lHz...
22
21
  # X-Originating-IP: [192.0.2.9]
23
22
  return nil unless mhead['x-ymailisg']
24
23
 
24
+ require 'sisimai/smtp/command'
25
25
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
26
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
27
- bodyslices = emailsteak[0].split("\n")
26
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
27
+ bodyslices = emailparts[0].split("\n")
28
28
  readcursor = 0 # (Integer) Points the current cursor position
29
29
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
30
30
  v = nil
31
31
 
32
32
  while e = bodyslices.shift do
33
- # Read error messages and delivery status lines from the head of the email
34
- # to the previous line of the beginning of the original message.
33
+ # Read error messages and delivery status lines from the head of the email to the previous
34
+ # line of the beginning of the original message.
35
35
  if readcursor == 0
36
36
  # Beginning of the bounce message or delivery status part
37
37
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
@@ -46,23 +46,24 @@ module Sisimai::Lhost
46
46
  # Remote host said: 550 5.1.1 <kijitora@example.org>... User Unknown [RCPT_TO]
47
47
  v = dscontents[-1]
48
48
 
49
- if cv = e.match(/\A[<](.+[@].+)[>]:[ \t]*\z/)
49
+ if e.start_with?('<') && Sisimai::String.aligned(e, ['<', '@', '>:'])
50
50
  # <kijitora@example.org>:
51
51
  if v['recipient']
52
52
  # There are multiple recipient addresses in the message body.
53
53
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
54
54
  v = dscontents[-1]
55
55
  end
56
- v['recipient'] = cv[1]
56
+ v['recipient'] = Sisimai::Address.s3s4(e[0, e.index('>:')])
57
57
  v['diagnosis'] = ''
58
58
  recipients += 1
59
+
59
60
  else
60
61
  if e.start_with?('Remote host said:')
61
62
  # Remote host said: 550 5.1.1 <kijitora@example.org>... User Unknown [RCPT_TO]
62
63
  v['diagnosis'] = e
63
64
 
64
65
  # Get SMTP command from the value of "Remote host said:"
65
- if cv = e.match(/\[([A-Z]{4}).*\]\z/) then v['command'] = cv[1] end
66
+ v['command'] = Sisimai::SMTP::Command.find(e)
66
67
  else
67
68
  # <mailboxfull@example.jp>:
68
69
  # Remote host said:
@@ -71,9 +72,9 @@ module Sisimai::Lhost
71
72
  if v['diagnosis'].start_with?('Remote host said:')
72
73
  # Remote host said:
73
74
  # 550 5.2.2 <mailboxfull@example.jp>... Mailbox Full
74
- if cv = e.match(/\[([A-Z]{4}).*\]\z/)
75
+ if cv = Sisimai::SMTP::Command.find(e)
75
76
  # [RCPT_TO]
76
- v['command'] = cv[1]
77
+ v['command'] = cv
77
78
  else
78
79
  # 550 5.2.2 <mailboxfull@example.jp>... Mailbox Full
79
80
  v['diagnosis'] = e
@@ -89,10 +90,10 @@ module Sisimai::Lhost
89
90
 
90
91
  dscontents.each do |e|
91
92
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].gsub(/\\n/, ' '))
92
- e['command'] ||= 'RCPT' if e['diagnosis'] =~ /[<].+[@].+[>]/
93
+ e['command'] ||= 'RCPT' if Sisimai::String.aligned(e['diagnosis'], ['<', '@', '>'])
93
94
  end
94
95
 
95
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
96
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
96
97
  end
97
98
  def description; return 'Yahoo! MAIL: https://www.yahoo.com'; end
98
99
  end