Module: Sisimai::Lhost::Courier
- Defined in:
- lib/sisimai/lhost/courier.rb
Overview
Sisimai::Lhost::Courier decodes a bounce email which created by Courier MTA www.courier-mta.org/. Methods in the module are called from only Sisimai::Message.
Constant Summary collapse
- Indicators =
Sisimai::Lhost.INDICATORS
- Boundaries =
['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
- StartingOf =
{ # courier/module.dsn/dsn*.txt message: ['DELAYS IN DELIVERING YOUR MESSAGE', 'UNDELIVERABLE MAIL'], }.freeze
- MessagesOf =
{ # courier/module.esmtp/esmtpclient.c:526| hard_error(del, ctf, "No such domain."); 'hostunknown' => ['No such domain.'], # courier/module.esmtp/esmtpclient.c:531| hard_error(del, ctf, # courier/module.esmtp/esmtpclient.c:532| "This domain's DNS violates RFC 1035."); 'systemerror' => ["This domain's DNS violates RFC 1035."], # courier/module.esmtp/esmtpclient.c:535| soft_error(del, ctf, "DNS lookup failed."); 'networkerror' => ['DNS lookup failed.'], }.freeze
Class Method Summary collapse
Class Method Details
.description ⇒ Object
143 |
# File 'lib/sisimai/lhost/courier.rb', line 143 def description; return 'Courier MTA'; end |
.inquire(mhead, mbody) ⇒ Hash, Nil
This method is abstract.
Decodes the bounce message from Courier MTA
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/sisimai/lhost/courier.rb', line 30 def inquire(mhead, mbody) match = 0 match += 1 if mhead['from'].include?('Courier mail server at ') match += 1 if mhead['subject'].include?('NOTICE: mail delivery status.') match += 1 if mhead['subject'].include?('WARNING: delayed mail.') if mhead['message-id'] # Message-ID: <courier.4D025E3A.00001792@5jo.example.org> match += 1 if mhead['message-id'].start_with?('<courier.') end return nil if match == 0 require 'sisimai/rfc1123' require 'sisimai/rfc1894' require 'sisimai/smtp/command' fieldtable = Sisimai::RFC1894.FIELDTABLE = {} # (Hash) Store values of each Per-Message field dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil emailparts = Sisimai::RFC5322.part(mbody, Boundaries) bodyslices = emailparts[0].split("\n") readslices = [''] readcursor = 0 # (Integer) Points the current cursor position recipients = 0 # (Integer) The number of 'Final-Recipient' header thecommand = '' # (String) SMTP Command name begin with the string '>>>' while e = bodyslices.shift do # Read error messages and delivery status lines from the head of the email to the previous # line of the beginning of the original message. readslices << e # Save the current line for the next loop if readcursor == 0 # Beginning of the bounce message or message/delivery-status part if e.include?(StartingOf[:message][0]) || e.include?(StartingOf[:message][1]) readcursor |= Indicators[:deliverystatus] next end end next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty? f = Sisimai::RFC1894.match(e) if f > 0 # "e" matched with any field defined in RFC3464 next unless o = Sisimai::RFC1894.field(e) v = dscontents[-1] case o[3] when "addr" # Final-Recipient: rfc822; kijitora@example.jp # X-Actual-Recipient: rfc822; kijitora@example.co.jp if o[0] == 'final-recipient' # Final-Recipient: rfc822; kijitora@example.jp if v["recipient"] != "" # There are multiple recipient addresses in the message body. dscontents << Sisimai::Lhost.DELIVERYSTATUS v = dscontents[-1] end v['recipient'] = o[2] recipients += 1 else # X-Actual-Recipient: rfc822; kijitora@example.co.jp v['alias'] = o[2] end when "code" # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown v['spec'] = o[1] v['diagnosis'] = o[2] else # Other DSN fields defined in RFC3464 next if fieldtable[o[0]].nil? next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false v[fieldtable[o[0]]] = o[2] next if f != 1 [fieldtable[o[0]]] = o[2] end else # The line does not begin with a DSN field defined in RFC3464 if e.start_with?('>>> ') # Your message to the following recipients cannot be delivered: # # <kijitora@example.co.jp>: # mx.example.co.jp [74.207.247.95]: # >>> RCPT TO:<kijitora@example.co.jp> # <<< 550 5.1.1 <kijitora@example.co.jp>... User Unknown # thecommand = Sisimai::SMTP::Command.find(e) else # Continued line of the value of Diagnostic-Code field next if readslices[-2].start_with?('Diagnostic-Code:') == false next if e.start_with?(' ') == false v['diagnosis'] += " " + e readslices[-1] = "Diagnostic-Code: #{e}" end end end return nil if recipients == 0 dscontents.each do |e| # Set default values if each value is empty. .each_key { |a| e[a] ||= [a] || '' } e['command'] = thecommand if e["command"].empty? e['diagnosis'] = e['diagnosis'] || '' MessagesOf.each_key do |r| # Verify each regular expression of session errors next if MessagesOf[r].none? { |a| e['diagnosis'].include?(a) } e['reason'] = r break end end return { 'ds' => dscontents, 'rfc822' => emailparts[1] } end |