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
data/lib/sisimai/arf.rb CHANGED
@@ -13,21 +13,21 @@ module Sisimai
13
13
  # OpenDMARC 1.3.0 uses: This is an authentication failure report for an email message received from IP
14
14
  # Abusix ARF uses this is an autogenerated email abuse complaint regarding your network.
15
15
  Indicators = Sisimai::Lhost.INDICATORS
16
- StartingOf = {
17
- rfc822: ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'],
18
- report: ['Content-Type: message/feedback-report'],
19
- message: [
20
- ['this is a', 'abuse report'],
21
- ['this is a', 'authentication', 'failure report'],
22
- ['this is a', ' report for'],
23
- ['this is an authentication', 'failure report'],
24
- ['this is an autogenerated email abuse complaint'],
25
- ['this is an email abuse report'],
26
- ],
27
- }.freeze
28
- ReportFrom = ['staff@hotmail.com', 'complaints@email-abuse.amazonses.com'];
29
- LongFields = Sisimai::RFC5322.LONGFIELDS
30
- RFC822Head = Sisimai::RFC5322.HEADERFIELDS
16
+ AbuseAddrs = ['staff@hotmail.com', 'complaints@email-abuse.amazonses.com'].freeze
17
+ ReportFrom = "Content-Type: message/feedback-report".freeze
18
+ Boundaries = [
19
+ "Content-Type: message/rfc822",
20
+ "Content-Type: text/rfc822-headers",
21
+ "Content-Type: text/rfc822-header", # ??
22
+ ].freeze
23
+ ARFPreface = [
24
+ ["this is a", "abuse report"],
25
+ ["this is a", "authentication", "failure report"],
26
+ ["this is a", " report for"],
27
+ ["this is an authentication", "failure report"],
28
+ ["this is an autogenerated email abuse complaint"],
29
+ ["this is an email abuse report"],
30
+ ].freeze
31
31
 
32
32
  def description; return 'Abuse Feedback Reporting Format'; end
33
33
 
@@ -39,23 +39,27 @@ module Sisimai
39
39
  return false unless heads
40
40
 
41
41
  # Content-Type: multipart/report; report-type=feedback-report; ...
42
- return true if Sisimai::String.aligned(heads['content-type'], ['report-type=', 'feedback-report'])
42
+ return true if Sisimai::String.aligned(heads["content-type"], ["report-type=", "feedback-report"])
43
43
 
44
- match = false
45
- if heads['content-type'].include?('multipart/mixed')
44
+ if heads["content-type"].include?("multipart/mixed")
46
45
  # Microsoft (Hotmail, MSN, Live, Outlook) uses its own report format.
47
46
  # Amazon SES Complaints bounces
48
47
  cv = Sisimai::Address.s3s4(heads['from'])
49
- if heads['subject'].include?('complaint about message from ')
48
+ if heads["subject"].include?("complaint about message from ")
50
49
  # From: staff@hotmail.com
51
50
  # From: complaints@email-abuse.amazonses.com
52
51
  # Subject: complaint about message from 192.0.2.1
53
- match = true if ReportFrom.any? { |a| cv.include?(a) }
52
+ return true if AbuseAddrs.any? { |a| cv.include?(a) }
54
53
  end
55
54
  end
56
- match = true if heads['x-apple-unsubscribe'] == 'true'
57
55
 
58
- return match
56
+ while true
57
+ # X-Apple-Unsubscribe: true
58
+ break unless heads.has_key?("x-apple-unsubscribe")
59
+ return true if heads["x-apple-unsubscribe"] == "true"
60
+ break
61
+ end
62
+ return false
59
63
  end
60
64
 
61
65
  # @abstract Detect an error for Feedback Loop
@@ -67,25 +71,16 @@ module Sisimai
67
71
  return nil unless self.is_arf(mhead)
68
72
 
69
73
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
70
- bodyslices = mbody.split("\n")
71
- rfc822part = '' # (String) message/rfc822-headers part
72
- previousfn = '' # (String) Previous field name
73
- readcursor = 0 # (Integer) Points the current cursor position
74
- recipients = 0 # (Integer) The number of 'Final-Recipient' header
75
- rcptintext = '' # (String) Recipient address in the message body
76
- commondata = {
77
- 'diagnosis' => '', # Error message
78
- 'from' => '', # Original-Mail-From:
79
- 'rhost' => '', # Reporting-MTA:
80
- }
81
- arfheaders = {
82
- 'feedbacktype' => nil, # FeedBack-Type:
83
- 'rhost' => nil, # Source-IP:
84
- 'agent' => nil, # User-Agent:
85
- 'date' => nil, # Arrival-Date:
86
- 'authres' => nil, # Authentication-Results:
87
- }
88
- v = nil
74
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
75
+ bodyslices = emailparts[0].split("\n")
76
+ reportpart = false
77
+ readcursor = 0 # Points the current cursor position
78
+ recipients = 0 # The number of 'Final-Recipient' header
79
+ timestamp0 = "" # The value of "Arrival-Date" or "Received-Date"
80
+ remotehost = "" # The value of "Source-IP" field
81
+ reportedby = "" # The value of "Reporting-MTA" field
82
+ anotherone = "" # Other fields(append to Diagnosis)
83
+ v = dscontents[-1]
89
84
 
90
85
  # 3.1. Required Fields
91
86
  #
@@ -112,71 +107,24 @@ module Sisimai
112
107
  # message-id of 0000-000000000000000000000000000000000@mx
113
108
  # received from IP address 192.0.2.1 on
114
109
  # Thu, 29 Apr 2010 00:00:00 +0900 (JST)
115
- p = e.downcase
116
- if commondata['diagnosis'].empty?
117
- commondata['diagnosis'] = e if StartingOf[:message].any? { |a| Sisimai::String.aligned(p, a) }
118
- end
119
-
120
110
  if readcursor == 0
121
111
  # Beginning of the bounce message or message/delivery-status part
122
- readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:report][0])
123
- end
124
-
125
- if (readcursor & Indicators[:'message-rfc822']) == 0
126
- # Beginning of the original message part
127
- if e.start_with?(StartingOf[:rfc822][0], StartingOf[:rfc822][1])
128
- readcursor |= Indicators[:'message-rfc822']
129
- next
112
+ r = e.downcase
113
+ ARFPreface.each do |f|
114
+ # Hello,
115
+ # this is an autogenerated email abuse complaint regarding your network.
116
+ next unless Sisimai::String.aligned(r, f)
117
+ readcursor |= Indicators[:deliverystatus]
118
+ v["diagnosis"] << " " + e
119
+ break
130
120
  end
121
+ next
131
122
  end
123
+ next unless readcursor & Indicators[:deliverystatus] > 0
124
+ next if e.empty?
125
+ if e == ReportFrom then reportpart = true; next; end
132
126
 
133
- if readcursor & Indicators[:'message-rfc822'] > 0
134
- # message/rfc822 OR text/rfc822-headers part
135
- if e.start_with?('X-HmXmrOriginalRecipient:')
136
- # Microsoft ARF: original recipient.
137
- dscontents[-1]['recipient'] = Sisimai::Address.s3s4(e[e.index(':') + 1, e.size])
138
- recipients += 1
139
-
140
- # The "X-HmXmrOriginalRecipient" header appears only once so we take this opportunity
141
- # to hard-code ARF headers missing in Microsoft's implementation.
142
- arfheaders['feedbacktype'] = 'abuse'
143
- arfheaders['agent'] = 'Microsoft Junk Mail Reporting Program'
144
-
145
- elsif e.start_with?('From: ')
146
- # Microsoft ARF: original sender.
147
- commondata['from'] = Sisimai::Address.s3s4(e[6, e.size]) if commondata['from'].empty?
148
- previousfn = 'from'
149
-
150
- elsif e.start_with?(' ')
151
- # Continued line from the previous line
152
- if previousfn == 'from'
153
- # Multiple lines at "From:" field
154
- commondata['from'] << e
155
- next
156
- else
157
- rfc822part << e + "\n" if LongFields[previousfn]
158
- next unless e.empty?
159
- end
160
- rcptintext << e if previousfn == 'to'
161
-
162
- else
163
- # Get required headers only
164
- (lhs, rhs) = e.split(/:[ ]/, 2)
165
- next unless lhs
166
- lhs.downcase!
167
-
168
- previousfn = ''
169
- next unless RFC822Head[lhs]
170
-
171
- previousfn = lhs
172
- rfc822part << e + "\n"
173
- rcptintext = rhs if lhs == 'to'
174
- end
175
- else
176
- # message/feedback-report part
177
- next unless readcursor & Indicators[:deliverystatus] > 0
178
- next if e.empty?
179
-
127
+ if reportpart
180
128
  # Feedback-Type: abuse
181
129
  # User-Agent: SomeGenerator/1.0
182
130
  # Version: 0.1
@@ -184,151 +132,114 @@ module Sisimai
184
132
  # Original-Rcpt-To: <kijitora@example.jp>
185
133
  # Received-Date: Thu, 29 Apr 2009 00:00:00 JST
186
134
  # Source-IP: 192.0.2.1
187
- v = dscontents[-1]
188
-
189
- if e.start_with?('Original-Rcpt-To: ', 'Redacted-Address: ')
135
+ if e.start_with?("Original-Rcpt-To: ") || e.start_with?("Removal-Recipient: ")
190
136
  # Original-Rcpt-To header field is optional and may appear any number of times as appropriate:
191
- # Original-Rcpt-To: <user@example.com>
192
- # Redacted-Address: localpart@
193
- if v['recipient']
137
+ # Original-Rcpt-To: <kijitora@example.jp>
138
+ # Removal-Recipient: user@example.com
139
+ cv = Sisimai::Address.s3s4(e[e.index(" ") + 1, e.size]); next unless Sisimai::Address.is_emailaddress(cv)
140
+ cw = dscontents.size; next if cw > 0 && cv == dscontents[cw - 1]["recipient"]
141
+
142
+ if v["recipient"] != ""
194
143
  # There are multiple recipient addresses in the message body.
195
144
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
196
145
  v = dscontents[-1]
197
146
  end
198
- v['recipient'] = Sisimai::Address.s3s4(e[e.index(' ') + 1, e.size])
147
+ v["recipient"] = cv
199
148
  recipients += 1
200
149
 
201
- elsif e.start_with?('Feedback-Type: ')
150
+ elsif e.start_with?("Feedback-Type: ")
202
151
  # The header field MUST appear exactly once.
203
152
  # Feedback-Type: abuse
204
- arfheaders['feedbacktype'] = e[e.index(' ') + 1, e.size]
153
+ v["feedbacktype"] = e[e.index(" ") + 1, e.size]
205
154
 
206
- elsif e.start_with?('Authentication-Results: ')
155
+ elsif e.start_with?("Authentication-Results: ")
207
156
  # "Authentication-Results" indicates the result of one or more authentication checks
208
157
  # run by the report generator.
209
- # Authentication-Results: mail.example.jp; spf=fail smtp.mail=spammer@example.com
210
- arfheaders['authres'] = e[e.index(' ') + 1, e.size]
158
+ #
159
+ # Authentication-Results: mail.example.com;
160
+ # spf=fail smtp.mail=somespammer@example.com
161
+ anotherone << e + ", "
211
162
 
212
- elsif e.start_with?('User-Agent: ')
163
+ elsif e.start_with?("User-Agent: ")
213
164
  # The header field MUST appear exactly once.
214
165
  # User-Agent: SomeGenerator/1.0
215
- arfheaders['agent'] = e[e.index(' ') + 1, e.size]
166
+ anotherone << e + ", "
216
167
 
217
- elsif e.start_with?('Received-Date: ', 'Arrival-Date: ')
168
+ elsif e.start_with?("Received-Date: ") || e.start_with?("Arrival-Date: ")
218
169
  # Arrival-Date header is optional and MUST NOT appear more than once.
219
170
  # Received-Date: Thu, 29 Apr 2010 00:00:00 JST
220
171
  # Arrival-Date: Thu, 29 Apr 2010 00:00:00 +0000
221
- arfheaders['date'] = e[e.index(' ') + 1, e.size]
172
+ timestamp0 = e[e.index(" ") + 1, e.size]
222
173
 
223
- elsif e.start_with?('Reporting-MTA: dns; ')
174
+ elsif e.start_with?("Reporting-MTA: ")
224
175
  # The header is optional and MUST NOT appear more than once.
225
176
  # Reporting-MTA: dns; mx.example.jp
226
- commondata['rhost'] = e[e.index(';') + 2, e.size]
177
+ cv = Sisimai::RFC1894.field(e); next if cv.size == 0
178
+ reportedby = cv[2]
227
179
 
228
- elsif e.start_with?('Source-IP: ')
180
+ elsif e.start_with?("Source-IP: ")
229
181
  # The header is optional and MUST NOT appear more than once.
230
182
  # Source-IP: 192.0.2.45
231
- arfheaders['rhost'] = e[e.index(' ') + 1, e.size]
183
+ remotehost = e[e.index(" ") + 1, e.size]
232
184
 
233
- elsif e.start_with?('Original-Mail-From: ')
185
+ elsif e.start_with?("Original-Mail-From: ")
234
186
  # the header is optional and MUST NOT appear more than once.
235
187
  # Original-Mail-From: <somespammer@example.net>
236
- commondata['from'] = Sisimai::Address.s3s4(e[e.index(' ') + 1, e.size]) if commondata['from'].empty?
237
-
188
+ anotherone << e + ", "
238
189
  end
239
- end
240
- end
241
-
242
- if arfheaders['feedbacktype'] == 'auth-failure' && arfheaders['authres']
243
- # Append the value of Authentication-Results header
244
- commondata['diagnosis'] << ' ' << arfheaders['authres']
245
- end
246
-
247
- if recipients == 0
248
- # The original recipient address was not found
249
- if Sisimai::String.aligned(rfc822part, ["\nTo: ", '@'])
250
- # pick the address from To: header in message/rfc822 part.
251
- p1 = rfc822part.index("\nTo: ") + 5
252
- p2 = rfc822part.index("\n", p1 + 1)
253
- cm = p2 > 0 ? p2 - p1 : 255
254
- dscontents[-1]['recipient'] = Sisimai::Address.s3s4(rfc822part[p1, cm])
255
- recipients = 1
190
+ else
191
+ # Messages before "Content-Type: message/feedback-report" part
192
+ v["diagnosis"] << " " + e
256
193
  end
257
194
 
258
- while true
259
- # Insert pseudo recipient address when there is no valid recipient address in the message
260
- # for example,
261
- # Date: Thu, 29 Apr 2015 23:34:45 +0000
262
- # To: "undisclosed"
263
- # Subject: Nyaan
264
- # Message-ID: <ffffffffffffffffffffffff00000000@example.net>
265
- dscontents[-1]['recipient'] ||= ''
266
- break if dscontents[-1]['recipient'].include?('@')
267
- dscontents[-1]['recipient'] = Sisimai::Address.undisclosed(true)
268
- recipients = 1
269
- break
270
- end
271
- end
272
-
273
- unless Sisimai::String.aligned(rfc822part, ['From: ', '@'])
274
- # There is no "From:" header in the original message. Append the value of "Original-Mail-From"
275
- # value as a sender address.
276
- rfc822part << 'From: ' << commondata['from'] + "\n" unless commondata['from'].empty?
277
- end
195
+ while recipients == 0
196
+ # There is no recipient address in the message
197
+ if mhead.has_key?("x-apple-unsubscribe")
198
+ # X-Apple-Unsubscribe: true
199
+ last unless mhead["x-apple-unsubscribe"] == "true"
200
+ last unless mhead["from"].include?('@')
201
+ dscontents[0]["recipient"] = mhead["from"]
202
+ dscontents[0]["diagnosis"] = Sisimai::String.sweep(emailparts[0])
203
+ dscontents[0]["feedbacktype"] = "opt-out"
278
204
 
279
- if mhead['subject'].include?('complaint about message from ')
280
- # Microsoft ARF: remote host address.
281
- arfheaders['rhost'] = mhead['subject'][mhead['subject'].rindex(' ') + 1, mhead['subject'].size]
282
- commondata['diagnosis'] =
283
- 'This is a Microsoft email abuse report for an email message received from IP' << arfheaders['rhost'] + ' on ' << mhead['date']
205
+ # Addpend To: field as a pseudo header
206
+ emailparts[1] = sprintf("To: <%s>\n", mhead["from"]) if emailparts[1].empty?
284
207
 
285
- elsif mhead['subject'].include?('unsubscribe')
286
- # Apple Mail sent this email to unsubscribe from the message
287
- while true
288
- # Subject: unsubscribe
289
- # Content-Type: text/plain; charset=UTF-8
290
- # Auto-Submitted: auto-replied
291
- # X-Apple-Unsubscribe: true
292
- #
293
- # Apple Mail sent this email to unsubscribe from the message
294
- break unless mhead['x-apple-unsubscribe']
295
- break unless mhead['x-apple-unsubscribe'] == 'true'
296
- break unless mbody.include?('Apple Mail sent this email to unsubscribe from the message');
297
-
298
- dscontents[-1]['recipient'] = Sisimai::Address.s3s4(mhead['from'])
299
- dscontents[-1]['feedbacktype'] = 'opt-out'
300
- break
208
+ else
209
+ # Pick it from the original message part
210
+ p1 = emailparts[1].index("\nTo:"); break if p1.nil?
211
+ p2 = emailparts[1].index("\n", p1 + 4); break if p2.nil?
212
+ cv = Sisimai::Address.s3s4(emailparts[1][p1 + 4, p2 - p1])
213
+
214
+ # There is no valid email address in the To: header of the original message such as
215
+ # To: <Undisclosed Recipients>
216
+ cv = Sisimai::Address.undisclosed("r") unless Sisimai::Address.is_emailaddress(cv)
217
+ dscontents[0]["recipient"] = cv
218
+ end
219
+ recipients += 1
301
220
  end
302
- end
303
-
304
- dscontents.each do |e|
305
- # AOL = http://forums.cpanel.net/f43/aol-brutal-work-71473.html
306
- e['recipient'] = Sisimai::Address.s3s4(rcptintext) if e['recipient'][-1, 1] == '@'
307
- arfheaders.each_key { |a| e[a] ||= arfheaders[a] || '' }
308
- e.delete('authres')
309
-
310
- e['diagnosis'] = commondata['diagnosis'] unless e['diagnosis']
311
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
312
- e['date'] = mhead['date'] if e['date'].empty?
313
- e['reason'] = 'feedback'
314
- e['agent'] = 'Feedback-Loop'
315
- %w[command action status alias].each { |a| e[a] = '' }
316
-
317
- # Get the remote IP address from the message body
318
- next unless e['rhost'].empty?
319
- if commondata['rhost'].size > 0
320
- # The value of "Reporting-MTA" header
321
- e['rhost'] = commondata['rhost']
322
-
323
- else
324
- # Try to get an IP address from the error message
325
- # This is an email abuse report for an email message received from IP address 24.64.1.1
326
- # on Thu, 29 Apr 2010 00:00:00 +0000
327
- ip = Sisimai::String.ipv4(e['diagnosis']) || []
328
- e['rhost'] = ip[0] if ip.size > 0
221
+ return nil if recipients == 0
222
+
223
+ anotherone = ": " + Sisimai::String.sweep(anotherone) if anotherone != ""
224
+ anotherone = anotherone.chop if anotherone[-1, 1] == ","
225
+
226
+ j = -1
227
+ dscontents.each do |e|
228
+ # Tidy up the error message in e.Diagnosis, Try to detect the bounce reason.
229
+ j += 1
230
+ e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"] + anotherone)
231
+ e["reason"] = "feedback"
232
+ e["rhost"] = remotehost
233
+ e["lhost"] = reportedby
234
+ e["date"] = timestamp0
235
+
236
+ # Copy some values from the previous element when the report have 2 or more email address
237
+ next if j == 0 || dscontents.size == 1
238
+ e["diagnosis"] = dscontents[j - 1]["diagnosis"]
239
+ e["feedbacktype"] = dscontents[j - 1]["feedbacktype"]
329
240
  end
330
241
  end
331
- return { 'ds' => dscontents, 'rfc822' => rfc822part }
242
+ return { "ds" => dscontents, "rfc822" => emailparts[1] }
332
243
  end
333
244
  end
334
245
  end