sisimai 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake-test.yml +1 -1
  3. data/ChangeLog.md +102 -0
  4. data/Makefile +4 -2
  5. data/README-JA.md +23 -16
  6. data/README.md +22 -15
  7. data/lib/sisimai/arf.rb +121 -210
  8. data/lib/sisimai/fact.rb +208 -158
  9. data/lib/sisimai/lda.rb +98 -0
  10. data/lib/sisimai/lhost/activehunter.rb +1 -1
  11. data/lib/sisimai/lhost/amazonses.rb +185 -301
  12. data/lib/sisimai/lhost/apachejames.rb +48 -51
  13. data/lib/sisimai/lhost/biglobe.rb +1 -2
  14. data/lib/sisimai/lhost/courier.rb +10 -8
  15. data/lib/sisimai/lhost/domino.rb +25 -25
  16. data/lib/sisimai/lhost/dragonfly.rb +3 -4
  17. data/lib/sisimai/lhost/einsundeins.rb +3 -4
  18. data/lib/sisimai/lhost/exchange2003.rb +6 -8
  19. data/lib/sisimai/lhost/exchange2007.rb +111 -101
  20. data/lib/sisimai/lhost/exim.rb +232 -242
  21. data/lib/sisimai/lhost/ezweb.rb +43 -51
  22. data/lib/sisimai/lhost/fml.rb +2 -3
  23. data/lib/sisimai/lhost/gmail.rb +32 -28
  24. data/lib/sisimai/lhost/gmx.rb +4 -16
  25. data/lib/sisimai/lhost/googlegroups.rb +9 -8
  26. data/lib/sisimai/lhost/googleworkspace.rb +94 -0
  27. data/lib/sisimai/lhost/imailserver.rb +7 -16
  28. data/lib/sisimai/lhost/interscanmss.rb +1 -1
  29. data/lib/sisimai/lhost/kddi.rb +3 -4
  30. data/lib/sisimai/lhost/mailfoundry.rb +2 -5
  31. data/lib/sisimai/lhost/mailmarshalsmtp.rb +1 -2
  32. data/lib/sisimai/lhost/messagingserver.rb +14 -13
  33. data/lib/sisimai/lhost/mfilter.rb +4 -3
  34. data/lib/sisimai/lhost/notes.rb +2 -4
  35. data/lib/sisimai/lhost/opensmtpd.rb +2 -2
  36. data/lib/sisimai/lhost/postfix.rb +25 -27
  37. data/lib/sisimai/lhost/qmail.rb +130 -106
  38. data/lib/sisimai/lhost/sendmail.rb +19 -18
  39. data/lib/sisimai/lhost/v5sendmail.rb +88 -60
  40. data/lib/sisimai/lhost/verizon.rb +2 -2
  41. data/lib/sisimai/lhost/x1.rb +1 -1
  42. data/lib/sisimai/lhost/x2.rb +1 -2
  43. data/lib/sisimai/lhost/x3.rb +2 -2
  44. data/lib/sisimai/lhost/x6.rb +1 -1
  45. data/lib/sisimai/lhost/zoho.rb +2 -2
  46. data/lib/sisimai/lhost.rb +18 -21
  47. data/lib/sisimai/message.rb +93 -146
  48. data/lib/sisimai/order.rb +21 -77
  49. data/lib/sisimai/reason/authfailure.rb +1 -4
  50. data/lib/sisimai/reason/badreputation.rb +2 -2
  51. data/lib/sisimai/reason/blocked.rb +7 -10
  52. data/lib/sisimai/reason/contenterror.rb +7 -1
  53. data/lib/sisimai/reason/exceedlimit.rb +1 -4
  54. data/lib/sisimai/reason/failedstarttls.rb +42 -0
  55. data/lib/sisimai/reason/filtered.rb +5 -4
  56. data/lib/sisimai/reason/hasmoved.rb +1 -2
  57. data/lib/sisimai/reason/hostunknown.rb +3 -3
  58. data/lib/sisimai/reason/mailboxfull.rb +2 -4
  59. data/lib/sisimai/reason/mailererror.rb +1 -2
  60. data/lib/sisimai/reason/mesgtoobig.rb +2 -4
  61. data/lib/sisimai/reason/norelaying.rb +2 -3
  62. data/lib/sisimai/reason/notaccept.rb +2 -3
  63. data/lib/sisimai/reason/notcompliantrfc.rb +10 -4
  64. data/lib/sisimai/reason/rejected.rb +1 -1
  65. data/lib/sisimai/reason/requireptr.rb +2 -2
  66. data/lib/sisimai/reason/securityerror.rb +1 -3
  67. data/lib/sisimai/reason/spamdetected.rb +6 -8
  68. data/lib/sisimai/reason/speeding.rb +1 -2
  69. data/lib/sisimai/reason/suppressed.rb +36 -0
  70. data/lib/sisimai/reason/suspend.rb +1 -3
  71. data/lib/sisimai/reason/systemerror.rb +5 -0
  72. data/lib/sisimai/reason/toomanyconn.rb +1 -2
  73. data/lib/sisimai/reason/userunknown.rb +1 -1
  74. data/lib/sisimai/reason/virusdetected.rb +5 -6
  75. data/lib/sisimai/reason.rb +77 -73
  76. data/lib/sisimai/rfc1123.rb +152 -0
  77. data/lib/sisimai/rfc1894.rb +102 -62
  78. data/lib/sisimai/rfc2045.rb +2 -1
  79. data/lib/sisimai/rfc3464/thirdparty.rb +102 -0
  80. data/lib/sisimai/rfc3464.rb +222 -343
  81. data/lib/sisimai/rfc3834.rb +1 -1
  82. data/lib/sisimai/rfc5322.rb +7 -17
  83. data/lib/sisimai/rfc791.rb +69 -0
  84. data/lib/sisimai/rhost/aol.rb +36 -0
  85. data/lib/sisimai/rhost/apple.rb +5 -2
  86. data/lib/sisimai/rhost/cox.rb +3 -2
  87. data/lib/sisimai/rhost/facebook.rb +100 -0
  88. data/lib/sisimai/rhost/franceptt.rb +3 -2
  89. data/lib/sisimai/rhost/godaddy.rb +3 -2
  90. data/lib/sisimai/rhost/google.rb +19 -17
  91. data/lib/sisimai/rhost/gsuite.rb +42 -0
  92. data/lib/sisimai/rhost/iua.rb +3 -3
  93. data/lib/sisimai/rhost/kddi.rb +3 -2
  94. data/lib/sisimai/rhost/messagelabs.rb +37 -0
  95. data/lib/sisimai/rhost/microsoft.rb +56 -49
  96. data/lib/sisimai/rhost/mimecast.rb +29 -27
  97. data/lib/sisimai/rhost/nttdocomo.rb +4 -3
  98. data/lib/sisimai/rhost/outlook.rb +36 -0
  99. data/lib/sisimai/rhost/spectrum.rb +3 -2
  100. data/lib/sisimai/rhost/tencent.rb +3 -2
  101. data/lib/sisimai/rhost/yahooinc.rb +4 -3
  102. data/lib/sisimai/rhost.rb +69 -39
  103. data/lib/sisimai/smtp/command.rb +31 -21
  104. data/lib/sisimai/smtp/failure.rb +103 -0
  105. data/lib/sisimai/smtp/reply.rb +29 -25
  106. data/lib/sisimai/smtp/status.rb +36 -19
  107. data/lib/sisimai/smtp/transcript.rb +15 -15
  108. data/lib/sisimai/string.rb +0 -46
  109. data/lib/sisimai/version.rb +1 -1
  110. data/set-of-emails/maildir/bsd/lhost-postfix-30.eml +81 -81
  111. data/set-of-emails/maildir/bsd/{lhost-aol-03.eml → rhost-aol-03.eml} +1264 -1264
  112. data/set-of-emails/maildir/bsd/{lhost-aol-04.eml → rhost-aol-04.eml} +1260 -1260
  113. data/set-of-emails/maildir/bsd/{lhost-aol-05.eml → rhost-aol-05.eml} +105 -105
  114. data/set-of-emails/maildir/bsd/{lhost-aol-06.eml → rhost-aol-06.eml} +105 -105
  115. data/set-of-emails/maildir/bsd/rhost-gsuite-01.eml +189 -0
  116. data/set-of-emails/maildir/bsd/rhost-gsuite-02.eml +180 -0
  117. data/set-of-emails/maildir/bsd/rhost-gsuite-03.eml +251 -0
  118. data/set-of-emails/maildir/bsd/rhost-gsuite-04.eml +211 -0
  119. data/set-of-emails/maildir/bsd/rhost-gsuite-05.eml +226 -0
  120. data/set-of-emails/maildir/bsd/rhost-gsuite-06.eml +257 -0
  121. data/set-of-emails/maildir/bsd/rhost-gsuite-07.eml +289 -0
  122. data/set-of-emails/maildir/bsd/rhost-gsuite-08.eml +231 -0
  123. data/set-of-emails/maildir/bsd/rhost-gsuite-09.eml +231 -0
  124. data/set-of-emails/maildir/bsd/rhost-gsuite-10.eml +254 -0
  125. data/set-of-emails/maildir/bsd/rhost-gsuite-11.eml +228 -0
  126. data/set-of-emails/maildir/bsd/rhost-gsuite-12.eml +271 -0
  127. data/set-of-emails/maildir/bsd/rhost-gsuite-13.eml +261 -0
  128. data/set-of-emails/maildir/bsd/rhost-gsuite-14.eml +273 -0
  129. data/set-of-emails/maildir/bsd/rhost-gsuite-15.eml +229 -0
  130. data/set-of-emails/maildir/bsd/{lhost-messagelabs-01.eml → rhost-messagelabs-01.eml} +93 -93
  131. data/set-of-emails/maildir/bsd/rhost-outlook-01.eml +72 -0
  132. data/set-of-emails/maildir/bsd/rhost-outlook-02.eml +72 -0
  133. data/set-of-emails/maildir/bsd/rhost-outlook-03.eml +72 -0
  134. data/set-of-emails/maildir/bsd/rhost-outlook-04.eml +79 -0
  135. data/set-of-emails/maildir/bsd/rhost-outlook-06.eml +75 -0
  136. data/set-of-emails/maildir/bsd/rhost-outlook-07.eml +70 -0
  137. data/set-of-emails/maildir/bsd/rhost-outlook-08.eml +70 -0
  138. data/set-of-emails/maildir/bsd/rhost-outlook-09.eml +56 -0
  139. data/set-of-emails/maildir/tmp/arf-22.eml +49 -0
  140. data/set-of-emails/maildir/tmp/arf-23.eml +49 -0
  141. data/set-of-emails/maildir/tmp/arf-24.eml +50 -0
  142. data/set-of-emails/maildir/tmp/lhost-exim-07.eml +28 -0
  143. metadata +73 -56
  144. data/lib/sisimai/lhost/amavis.rb +0 -163
  145. data/lib/sisimai/lhost/amazonworkmail.rb +0 -127
  146. data/lib/sisimai/lhost/aol.rb +0 -125
  147. data/lib/sisimai/lhost/barracuda.rb +0 -92
  148. data/lib/sisimai/lhost/bigfoot.rb +0 -125
  149. data/lib/sisimai/lhost/facebook.rb +0 -188
  150. data/lib/sisimai/lhost/gsuite.rb +0 -194
  151. data/lib/sisimai/lhost/mailru.rb +0 -214
  152. data/lib/sisimai/lhost/mcafee.rb +0 -109
  153. data/lib/sisimai/lhost/messagelabs.rb +0 -120
  154. data/lib/sisimai/lhost/mxlogic.rb +0 -198
  155. data/lib/sisimai/lhost/office365.rb +0 -252
  156. data/lib/sisimai/lhost/outlook.rb +0 -129
  157. data/lib/sisimai/lhost/powermta.rb +0 -118
  158. data/lib/sisimai/lhost/receivingses.rb +0 -126
  159. data/lib/sisimai/lhost/sendgrid.rb +0 -150
  160. data/lib/sisimai/lhost/surfcontrol.rb +0 -105
  161. data/lib/sisimai/lhost/x4.rb +0 -269
  162. data/lib/sisimai/lhost/x5.rb +0 -112
  163. data/lib/sisimai/lhost/yahoo.rb +0 -102
  164. data/lib/sisimai/lhost/yandex.rb +0 -118
  165. data/lib/sisimai/mda.rb +0 -121
  166. data/lib/sisimai/smtp/error.rb +0 -119
  167. /data/set-of-emails/maildir/bsd/{lhost-googlegroups-15.eml → lhost-googleworkspace-01.eml} +0 -0
  168. /data/set-of-emails/maildir/bsd/{lhost-x4-08.eml → lhost-x2-06.eml} +0 -0
  169. /data/set-of-emails/maildir/bsd/{lhost-gsuite-01.eml → rfc3464-51.eml} +0 -0
  170. /data/set-of-emails/maildir/bsd/{lhost-gsuite-03.eml → rfc3464-52.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{lhost-gsuite-04.eml → rfc3464-53.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{lhost-gsuite-05.eml → rfc3464-54.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{lhost-gsuite-06.eml → rfc3464-55.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{lhost-gsuite-07.eml → rfc3464-56.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{lhost-gsuite-08.eml → rfc3464-57.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{lhost-gsuite-09.eml → rfc3464-58.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{lhost-gsuite-10.eml → rfc3464-59.eml} +0 -0
  178. /data/set-of-emails/maildir/bsd/{lhost-gsuite-11.eml → rfc3464-60.eml} +0 -0
  179. /data/set-of-emails/maildir/bsd/{lhost-gsuite-12.eml → rfc3464-61.eml} +0 -0
  180. /data/set-of-emails/maildir/bsd/{lhost-gsuite-13.eml → rfc3464-62.eml} +0 -0
  181. /data/set-of-emails/maildir/bsd/{lhost-gsuite-14.eml → rfc3464-63.eml} +0 -0
  182. /data/set-of-emails/maildir/bsd/{lhost-gsuite-15.eml → rfc3464-64.eml} +0 -0
  183. /data/set-of-emails/maildir/bsd/{lhost-gsuite-02.eml → rfc3464-65.eml} +0 -0
  184. /data/set-of-emails/maildir/bsd/{lhost-aol-01.eml → rhost-aol-01.eml} +0 -0
  185. /data/set-of-emails/maildir/bsd/{lhost-aol-02.eml → rhost-aol-02.eml} +0 -0
  186. /data/set-of-emails/maildir/bsd/{lhost-facebook-03.eml → rhost-facebook-03.eml} +0 -0
  187. /data/set-of-emails/maildir/bsd/{lhost-facebook-04.eml → rhost-facebook-04.eml} +0 -0
  188. /data/set-of-emails/maildir/bsd/{lhost-messagelabs-02.eml → rhost-messagelabs-02.eml} +0 -0
  189. /data/set-of-emails/maildir/bsd/{lhost-messagelabs-03.eml → rhost-messagelabs-03.eml} +0 -0
  190. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-37.eml +0 -0
  191. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-38.eml +0 -0
  192. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-39.eml +0 -0
@@ -1,198 +0,0 @@
1
- module Sisimai::Lhost
2
- # Sisimai::Lhost::MXLogic decodes a bounce email which created by McAfee SaaS (formerly MXLogic).
3
- # Methods in the module are called from only Sisimai::Message.
4
- module MXLogic
5
- class << self
6
- # Based on Sisimai::Lhost::Exim
7
- require 'sisimai/lhost'
8
-
9
- Indicators = Sisimai::Lhost.INDICATORS
10
- Boundaries = ['Included is a copy of the message header:'].freeze
11
- StartingOf = { message: ['This message was created automatically by mail delivery software.'] }.freeze
12
- ReCommands = [
13
- %r/SMTP error from remote (?:mail server|mailer) after ([A-Za-z]{4})/,
14
- %r/SMTP error from remote (?:mail server|mailer) after end of ([A-Za-z]{4})/,
15
- ].freeze
16
- MessagesOf = {
17
- # % find exim/ -type f -exec grep 'message = US' {} /dev/null \;
18
- # route.c:1158| DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
19
- 'userunknown' => ['user not found'],
20
- # transports/smtp.c:3524| addr->message = US"all host address lookups failed permanently";
21
- # routers/dnslookup.c:331| addr->message = US"all relevant MX records point to non-existent hosts";
22
- # route.c:1826| uschar *message = US"Unrouteable address";
23
- 'hostunknown' => [
24
- 'all host address lookups failed permanently',
25
- 'all relevant MX records point to non-existent hosts',
26
- 'Unrouteable address',
27
- ],
28
- # transports/appendfile.c:2567| addr->user_message = US"mailbox is full";
29
- # transports/appendfile.c:3049| addr->message = string_sprintf("mailbox is full "
30
- # transports/appendfile.c:3050| "(quota exceeded while writing to file %s)", filename);
31
- 'mailboxfull' => ['mailbox is full', 'error: quota exceed'],
32
- # routers/dnslookup.c:328| addr->message = US"an MX or SRV record indicated no SMTP service";
33
- # transports/smtp.c:3502| addr->message = US"no host found for existing SMTP connection";
34
- 'notaccept' => [
35
- 'an MX or SRV record indicated no SMTP service',
36
- 'no host found for existing SMTP connection',
37
- ],
38
- # parser.c:666| *errorptr = string_sprintf("%s (expected word or \"<\")", *errorptr);
39
- # parser.c:701| if(bracket_count++ > 5) FAILED(US"angle-brackets nested too deep");
40
- # parser.c:738| FAILED(US"domain missing in source-routed address");
41
- # parser.c:747| : string_sprintf("malformed address: %.32s may not follow %.*s",
42
- 'syntaxerror' => [
43
- 'angle-brackets nested too deep',
44
- 'expected word or "<"',
45
- 'domain missing in source-routed address',
46
- 'malformed address:',
47
- ],
48
- # deliver.c:5614| addr->message = US"delivery to file forbidden";
49
- # deliver.c:5624| addr->message = US"delivery to pipe forbidden";
50
- # transports/pipe.c:1156| addr->user_message = US"local delivery failed";
51
- 'systemerror' => [
52
- 'delivery to file forbidden',
53
- 'delivery to pipe forbidden',
54
- 'local delivery failed',
55
- 'LMTP error after ',
56
- ],
57
- # deliver.c:5425| new->message = US"Too many \"Received\" headers - suspected mail loop";
58
- 'contenterror' => ['Too many "Received" headers'],
59
- }.freeze
60
- DelayedFor = [
61
- 'retry timeout exceeded',
62
- 'No action is required on your part',
63
- 'retry time not reached for any host after a long failure period',
64
- 'all hosts have been failing for a long time and were last tried',
65
- 'Delay reason: ',
66
- 'has been frozen',
67
- 'was frozen on arrival by ',
68
- ].freeze
69
-
70
- # @abstract Decodes the bounce message from McAfee Saas (Formerly MXLogic)
71
- # @param [Hash] mhead Message headers of a bounce email
72
- # @param [String] mbody Message body of a bounce email
73
- # @return [Hash] Bounce data list and message/rfc822 part
74
- # @return [Nil] it failed to decode or the arguments are missing
75
- def inquire(mhead, mbody)
76
- # X-MX-Bounce: mta/src/queue/bounce
77
- # X-MXL-NoteHash: ffffffffffffffff-0000000000000000000000000000000000000000
78
- # X-MXL-Hash: 4c9d4d411993da17-bbd4212b6c887f6c23bab7db4bd87ef5edc00758
79
- match = 0
80
- match += 1 if mhead['x-mx-bounce']
81
- match += 1 if mhead['x-mxl-hash']
82
- match += 1 if mhead['x-mxl-notehash']
83
- match += 1 if mhead['from'].start_with?('Mail Delivery System')
84
- match += 1 if ['Delivery Status Notification', 'Mail delivery failed', 'Warning: message '].any? { |a| mhead['subject'].include?(a) }
85
- return nil unless match > 0
86
-
87
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
88
- emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
89
- bodyslices = emailparts[0].split("\n")
90
- readcursor = 0 # (Integer) Points the current cursor position
91
- recipients = 0 # (Integer) The number of 'Final-Recipient' header
92
- v = nil
93
-
94
- while e = bodyslices.shift do
95
- # Read error messages and delivery status lines from the head of the email to the previous
96
- # line of the beginning of the original message.
97
- if readcursor == 0
98
- # Beginning of the bounce message or delivery status part
99
- readcursor |= Indicators[:deliverystatus] if e == StartingOf[:message][0]
100
- next
101
- end
102
- next if (readcursor & Indicators[:deliverystatus]) == 0
103
- next if e.empty?
104
-
105
- # This message was created automatically by mail delivery software.
106
- #
107
- # A message that you sent could not be delivered to one or more of its
108
- # recipients. This is a permanent error. The following address(es) failed:
109
- #
110
- # kijitora@example.jp
111
- # SMTP error from remote mail server after RCPT TO:<kijitora@example.jp>:
112
- # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
113
- v = dscontents[-1]
114
-
115
- if e.start_with?(' <') && e.include?('@') && e.include?('>:')
116
- # A message that you have sent could not be delivered to one or more
117
- # recipients. This is a permanent error. The following address failed:
118
- #
119
- # <kijitora@example.co.jp>: 550 5.1.1 ...
120
- if v['recipient']
121
- # There are multiple recipient addresses in the message body.
122
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
123
- v = dscontents[-1]
124
- end
125
- v['recipient'] = e[3, e.index('>:') - 3]
126
- v['diagnosis'] = e[e.index('>:') + 3, e.size]
127
- recipients += 1
128
-
129
- elsif dscontents.size == recipients
130
- # Error message
131
- next if e.empty?
132
- v['diagnosis'] << e + ' '
133
- end
134
- end
135
- return nil unless recipients > 0
136
-
137
- # Get the name of the local MTA
138
- # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
139
- receivedby = mhead['received'] || []
140
- recvdtoken = Sisimai::RFC5322.received(receivedby[-1])
141
-
142
- dscontents.each do |e|
143
- # Check the error message, the rhost, the lhost, and the smtp command.
144
- e['lhost'] = recvdtoken[0]
145
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].gsub(/[-]{2}.*\z/, ''))
146
-
147
- unless e['rhost']
148
- # Get the remote host name
149
- p1 = e['diagnosis'].index('host ') || -1
150
- p2 = e['diagnosis'].index(' ', p1 + 5)
151
-
152
- # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
153
- e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
154
- e['rhost'] ||= recvdtoken[1]
155
- end
156
-
157
- unless e['command']
158
- # Get the SMTP command name for the session
159
- ReCommands.each do |r|
160
- # Verify each regular expression of SMTP commands
161
- if cv = e['diagnosis'].match(r)
162
- e['command'] = cv[1].upcase
163
- break
164
- end
165
- end
166
-
167
- # Detect the reason of bounce
168
- if e['command'] == 'MAIL'
169
- # MAIL | Connected to 192.0.2.135 but sender was rejected.
170
- e['reason'] = 'rejected'
171
-
172
- elsif %w[HELO EHLO].index(e['command'])
173
- # HELO | Connected to 192.0.2.135 but my name was rejected.
174
- e['reason'] = 'blocked'
175
- else
176
- # Verify each regular expression of session errors
177
- MessagesOf.each_key do |r|
178
- # Check each regular expression
179
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
180
- e['reason'] = r
181
- break
182
- end
183
-
184
- unless e['reason']
185
- # The reason "expired"
186
- e['reason'] = 'expired' if DelayedFor.any? { |a| e['diagnosis'].include?(a) }
187
- end
188
- end
189
- end
190
- end
191
-
192
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
193
- end
194
- def description; return 'McAfee SaaS'; end
195
- end
196
- end
197
- end
198
-
@@ -1,252 +0,0 @@
1
- module Sisimai::Lhost
2
- # Sisimai::Lhost::Office365 decodes a bounce email which created by Microsoft 365 https://office.microsoft.com/'.
3
- # Methods in the module are called from only Sisimai::Message.
4
- module Office365
5
- class << self
6
- require 'sisimai/lhost'
7
-
8
- Indicators = Sisimai::Lhost.INDICATORS
9
- Boundaries = ['Content-Type: message/rfc822', 'Original message headers:'].freeze
10
- StartingOf = {
11
- error: ['Diagnostic information for administrators:'],
12
- eoerr: ['Original message headers:'],
13
- }.freeze
14
- MarkingsOf = {
15
- eoe: %r{\A(?:
16
- Original[ ][Mm]essage[ ][Hh]eaders:?
17
- |Message[ ]Hops
18
- |Cabe.+alhos[ ]originais[ ]da[ ]mensagem:
19
- |Oorspronkelijke[ ]berichtkoppen:
20
- )
21
- }x,
22
- rfc3464: %r|\AContent-Type:[ ]message/delivery-status|,
23
- lhost: %r{\A(?:
24
- Generating[ ]server
25
- |Bronserver
26
- |Servidor[ ]de[ ]origem
27
- ):[ ](.+)\z
28
- }x,
29
- error: %r{\A(?:
30
- Diagnostic[ ]information[ ]for[ ]administrators:
31
- |Error[ ]Details
32
- |Diagnostische[ ]gegevens[ ]voor[ ]beheerders:
33
- |Informa.+es[ ]de[ ]diagn.+stico[ ]para[ ]administradores:
34
- )
35
- }x,
36
- message: %r{\A(?:
37
- Delivery[ ]has[ ]failed[ ]to[ ]these[ ]recipients[ ]or[ ]groups:
38
- |Original[ ]Message[ ]Details
39
- |.+[ ]rejected[ ]your[ ]message[ ]to[ ]the[ ]following[ ]e[-]?mail[ ]addresses:
40
- |Falha[ ]na[ ]entrega[ ]a[ ]estes[ ]destinat.+rios[ ]ou[ ]grupos:
41
- |Uw[ ]bericht[ ]kan[ ]niet[ ]worden[ ]bezorgd[ ]bij[ ]de[ ]volgende[ ]geadresseerden[ ]of[ ]groepen:
42
- )
43
- }x,
44
- }.freeze
45
- Headers365 = %w[
46
- x-ms-exchange-message-is-ndr
47
- x-microsoft-antispam-prvs
48
- x-exchange-antispam-report-test
49
- x-exchange-antispam-report-cfa-test
50
- x-ms-exchange-crosstenant-originalarrivaltime
51
- x-ms-exchange-crosstenant-fromentityheader
52
- x-ms-exchange-transport-crosstenantheadersstamped
53
- ]
54
- StatusList = {
55
- # https://support.office.com/en-us/article/Email-non-delivery-reports-in-Office-365-51daa6b9-2e35-49c4-a0c9-df85bf8533c3
56
- %r/\A4[.]4[.]7\z/ => 'expired',
57
- %r/\A4[.]4[.]312\z/ => 'networkerror',
58
- %r/\A4[.]4[.]316\z/ => 'expired',
59
- %r/\A4[.]7[.]26\z/ => 'authfailure',
60
- %r/\A4[.]7[.][56]\d\d\z/ => 'blocked',
61
- %r/\A4[.]7[.]8[5-9]\d\z/ => 'blocked',
62
- %r/\A5[.]0[.]350\z/ => 'contenterror',
63
- %r/\A5[.]1[.]10\z/ => 'userunknown',
64
- %r/\A5[.]4[.]1\z/ => 'norelaying',
65
- %r/\A5[.]4[.]312\z/ => 'networkerror',
66
- %r/\A5[.]4[.]316\z/ => 'expired',
67
- %r/\A5[.]4[.]6\z/ => 'networkerror',
68
- %r/\A5[.]6[.]11\z/ => 'contenterror',
69
- %r/\A5[.]7[.]1\z/ => 'rejected',
70
- %r/\A5[.]7[.]1[23]\z/ => 'rejected',
71
- %r/\A5[.]7[.]124\z/ => 'rejected',
72
- %r/\A5[.]7[.]13[3-6]\z/ => 'rejected',
73
- %r/\A5[.]7[.]23\z/ => 'authfailure',
74
- %r/\A5[.]7[.]25\z/ => 'networkerror',
75
- %r/\A5[.]7[.]50[1-3]\z/ => 'spamdetected',
76
- %r/\A5[.]7[.]50[4-5]\z/ => 'filtered',
77
- %r/\A5[.]7[.]50[6-7]\z/ => 'blocked',
78
- %r/\A5[.]7[.]508\z/ => 'toomanyconn',
79
- %r/\A5[.]7[.]509\z/ => 'authfailure',
80
- %r/\A5[.]7[.]510\z/ => 'notaccept',
81
- %r/\A5[.]7[.]511\z/ => 'rejected',
82
- %r/\A5[.]7[.]512\z/ => 'authfailure',
83
- %r/\A5[.]7[.]57\z/ => 'securityerror',
84
- %r/\A5[.]7[.]60[6-9]\z/ => 'blocked',
85
- %r/\A5[.]7[.]6[1-4]\d\z/ => 'blocked',
86
- %r/\A5[.]7[.]7[0-4]\d\z/ => 'toomanyconn',
87
- }.freeze
88
- ReCommands = {
89
- RCPT: %r/unknown recipient or mailbox unavailable ->.+[<]?.+[@].+[.][a-zA-Z]+[>]?/,
90
- }.freeze
91
-
92
- # @abstract decodes the bounce message from Microsoft 365
93
- # @param [Hash] mhead Message headers of a bounce email
94
- # @param [String] mbody Message body of a bounce email
95
- # @return [Hash] Bounce data list and message/rfc822 part
96
- # @return [Nil] it failed to decode or the arguments are missing
97
- def inquire(mhead, mbody)
98
- # X-MS-Exchange-Message-Is-Ndr:
99
- # X-Microsoft-Antispam-PRVS: <....@...outlook.com>
100
- # X-Exchange-Antispam-Report-Test: UriScan:;
101
- # X-Exchange-Antispam-Report-CFA-Test:
102
- # X-MS-Exchange-CrossTenant-OriginalArrivalTime: 29 Apr 2015 23:34:45.6789 (JST)
103
- # X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted
104
- # X-MS-Exchange-Transport-CrossTenantHeadersStamped: ...
105
- tryto = %r/.+[.](?:outbound[.]protection|prod)[.]outlook[.]com\b/
106
- match = 0
107
- match += 1 if mhead['subject'].include?('Undeliverable:')
108
- match += 1 if mhead['subject'].include?('Onbestelbaar:')
109
- match += 1 if mhead['subject'].include?('Não_entregue:')
110
-
111
- Headers365.each do |e|
112
- next if mhead[e].nil?
113
- next if mhead[e].empty?
114
- match += 1
115
- end
116
- match += 1 if mhead['received'].any? { |a| a =~ tryto }
117
- if mhead['message-id']
118
- # Message-ID: <00000000-0000-0000-0000-000000000000@*.*.prod.outlook.com>
119
- match += 1 if mhead['message-id'] =~ tryto
120
- end
121
- return nil if match < 2
122
-
123
- require 'sisimai/rfc1894'
124
- fieldtable = Sisimai::RFC1894.FIELDTABLE
125
- permessage = {} # (Hash) Store values of each Per-Message field
126
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
127
- emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
128
- bodyslices = emailparts[0].split("\n")
129
- readcursor = 0 # (Integer) Points the current cursor position
130
- recipients = 0 # (Integer) The number of 'Final-Recipient' header
131
- connheader = {}
132
- endoferror = false # (Boolean) Flag for the end of error messages
133
- htmlbegins = false # (Boolean) Flag for HTML part
134
- v = nil
135
-
136
- while e = bodyslices.shift do
137
- # Read error messages and delivery status lines from the head of the email to the previous
138
- # line of the beginning of the original message.
139
- if readcursor == 0
140
- # Beginning of the bounce message or delivery status part
141
- readcursor |= Indicators[:deliverystatus] if e =~ MarkingsOf[:message]
142
- next
143
- end
144
- next if (readcursor & Indicators[:deliverystatus]) == 0
145
- next if e.empty?
146
-
147
- # kijitora@example.com<mailto:kijitora@example.com>
148
- # The email address wasn't found at the destination domain. It might
149
- # be misspelled or it might not exist any longer. Try retyping the
150
- # address and resending the message.
151
- #
152
- # Original Message Details
153
- # Created Date: 4/29/2017 6:40:30 AM
154
- # Sender Address: neko@example.jp
155
- # Recipient Address: kijitora@example.org
156
- # Subject: Nyaan#
157
- v = dscontents[-1]
158
-
159
- if cv = e.match(/\A.+[@].+[<]mailto:(.+[@].+)[>]\z/) ||
160
- e.match(/\ARecipient[ ]Address:[ ]+(.+)\z/)
161
- # kijitora@example.com<mailto:kijitora@example.com>
162
- if v['recipient']
163
- # There are multiple recipient addresses in the message body.
164
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
165
- v = dscontents[-1]
166
- end
167
- v['recipient'] = cv[1]
168
- recipients += 1
169
-
170
- elsif cv = e.match(MarkingsOf[:lhost])
171
- # Generating server: FFFFFFFFFFFF.e0.prod.outlook.com
172
- connheader['lhost'] = cv[1].downcase
173
- else
174
- if endoferror
175
- # After "Original message headers:"
176
- next unless f = Sisimai::RFC1894.match(e)
177
- next unless o = Sisimai::RFC1894.field(e)
178
- next unless fieldtable[o[0]]
179
-
180
- if v['diagnosis']
181
- # Do not capture "Diagnostic-Code:" field because error message have already
182
- # been captured
183
- next if o[0] =~ /\A(?:diagnostic-code|final-recipient)\z/
184
- v[fieldtable[o[0]]] = o[2]
185
-
186
- next unless f
187
- permessage[fieldtable[o[0]]] = o[2]
188
- else
189
- # Capture "Diagnostic-Code:" field because no error messages have been captured
190
- v[fieldtable[o[0]]] = o[2]
191
- permessage[fieldtable[o[0]]] = o[2]
192
- end
193
- else
194
- if e =~ MarkingsOf[:error]
195
- # Diagnostic information for administrators:
196
- v['diagnosis'] = e
197
- else
198
- # kijitora@example.com
199
- # Remote Server returned '550 5.1.10 RESOLVER.ADR.RecipientNotFound; Recipien=
200
- # t not found by SMTP address lookup'
201
- if v['diagnosis']
202
- # The error message text have already captured
203
- if e =~ MarkingsOf[:eoe]
204
- # Original message headers:
205
- endoferror = true
206
- next
207
- end
208
- v['diagnosis'] << ' ' << e
209
- else
210
- # The error message text has not been captured yet
211
- endoferror = true if e =~ MarkingsOf[:rfc3464]
212
- end
213
- end
214
- end
215
- end
216
- end
217
- return nil unless recipients > 0
218
-
219
- dscontents.each do |e|
220
- # Set default values if each value is empty.
221
- permessage.each_key { |a| e[a] ||= permessage[a] || '' }
222
-
223
- e['status'] ||= ''
224
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
225
- if e['status'].empty? || e['status'].end_with?('.0.0')
226
- # There is no value of Status header or the value is 5.0.0, 4.0.0
227
- e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || e['status']
228
- end
229
-
230
- ReCommands.each_key do |p|
231
- # Try to match with regular expressions defined in ReCommands
232
- next unless e['diagnosis'] =~ ReCommands[p]
233
- e['command'] = p.to_s
234
- break
235
- end
236
- next unless e['status']
237
-
238
- StatusList.each_key do |f|
239
- # Try to match with each key as a regular expression
240
- next unless e['status'] =~ f
241
- e['reason'] = StatusList[f]
242
- break
243
- end
244
- end
245
-
246
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
247
- end
248
- def description; return 'Microsoft Office 365: https://office.microsoft.com/'; end
249
- end
250
- end
251
- end
252
-
@@ -1,129 +0,0 @@
1
- module Sisimai::Lhost
2
- # Sisimai::Lhost::Outlook decode a bounce email which created by Microsoft outlook.com https://www.outlook.com/.
3
- # Methods in the module are called from only Sisimai::Message.
4
- module Outlook
5
- class << self
6
- require 'sisimai/lhost'
7
-
8
- Indicators = Sisimai::Lhost.INDICATORS
9
- Boundaries = ['Content-Type: message/rfc822'].freeze
10
- StartingOf = { message: ['This is an automatically generated Delivery Status Notification'] }.freeze
11
- MessagesOf = {
12
- 'hostunknown' => ['The mail could not be delivered to the recipient because the domain is not reachable'],
13
- 'userunknown' => ['Requested action not taken: mailbox unavailable'],
14
- }.freeze
15
-
16
- # @abstract Decodes the bounce message from Microsoft Outlook.com
17
- # @param [Hash] mhead Message headers of a bounce email
18
- # @param [String] mbody Message body of a bounce email
19
- # @return [Hash] Bounce data list and message/rfc822 part
20
- # @return [Nil] it failed to decode or the arguments are missing
21
- def inquire(mhead, mbody)
22
- # X-Message-Delivery: Vj0xLjE7RD0wO0dEPTA7U0NMPTk7bD0xO3VzPTE=
23
- # X-Message-Info: AuEzbeVr9u5fkDpn2vR5iCu5wb6HBeY4iruBjnutBzpStnUabbM...
24
- match = 0
25
- match += 1 if mhead['subject'].start_with?('Delivery Status Notification')
26
- match += 1 if mhead['x-message-delivery']
27
- match += 1 if mhead['x-message-info']
28
- match += 1 if mhead['received'].any? { |a| a.include?('.hotmail.com') }
29
- return nil if match < 2
30
-
31
- fieldtable = Sisimai::RFC1894.FIELDTABLE
32
- permessage = {} # (Hash) Store values of each Per-Message field
33
-
34
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
35
- emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
36
- bodyslices = emailparts[0].split("\n")
37
- readslices = ['']
38
- readcursor = 0 # (Integer) Points the current cursor position
39
- recipients = 0 # (Integer) The number of 'Final-Recipient' header
40
- v = nil
41
-
42
- while e = bodyslices.shift do
43
- # Read error messages and delivery status lines from the head of the email to the previous
44
- # line of the beginning of the original message.
45
- readslices << e # Save the current line for the next loop
46
-
47
- if readcursor == 0
48
- # Beginning of the bounce message or message/delivery-status part
49
- readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
50
- next
51
- end
52
- next if (readcursor & Indicators[:deliverystatus]) == 0
53
- next if e.empty?
54
-
55
- if f = Sisimai::RFC1894.match(e)
56
- # "e" matched with any field defined in RFC3464
57
- next unless o = Sisimai::RFC1894.field(e)
58
- v = dscontents[-1]
59
-
60
- if o[-1] == 'addr'
61
- # Final-Recipient: rfc822; kijitora@example.jp
62
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
63
- if o[0] == 'final-recipient'
64
- # Final-Recipient: rfc822; kijitora@example.jp
65
- if v['recipient']
66
- # There are multiple recipient addresses in the message body.
67
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
68
- v = dscontents[-1]
69
- end
70
- v['recipient'] = o[2]
71
- recipients += 1
72
- else
73
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
74
- v['alias'] = o[2]
75
- end
76
- elsif o[-1] == 'code'
77
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
78
- v['spec'] = o[1]
79
- v['diagnosis'] = o[2]
80
- else
81
- # Other DSN fields defined in RFC3464
82
- next unless fieldtable[o[0]]
83
- v[fieldtable[o[0]]] = o[2]
84
-
85
- next unless f
86
- permessage[fieldtable[o[0]]] = o[2]
87
- end
88
- else
89
- # Continued line of the value of Diagnostic-Code field
90
- next unless readslices[-2].start_with?('Diagnostic-Code:')
91
- next unless e.start_with?(' ')
92
- v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
93
- readslices[-1] = 'Diagnostic-Code: ' << e
94
- end
95
- end
96
- return nil unless recipients > 0
97
-
98
- dscontents.each do |e|
99
- # Set default values if each value is empty.
100
- permessage.each_key { |a| e[a] ||= permessage[a] || '' }
101
-
102
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
103
- if e['diagnosis'].empty?
104
- # No message in 'diagnosis'
105
- if e['action'] == 'delayed'
106
- # Set pseudo diagnostic code message for delaying
107
- e['diagnosis'] = 'Delivery to the following recipients has been delayed.'
108
- else
109
- # Set pseudo diagnostic code message
110
- e['diagnosis'] = 'Unable to deliver message to the following recipients, '
111
- e['diagnosis'] << 'due to being unable to connect successfully to the destination mail server.'
112
- end
113
- end
114
-
115
- MessagesOf.each_key do |r|
116
- # Verify each regular expression of session errors
117
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
118
- e['reason'] = r
119
- break
120
- end
121
- end
122
-
123
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
124
- end
125
- def description; return 'Microsoft Outlook.com: https://www.outlook.com/'; end
126
- end
127
- end
128
- end
129
-