Module: Sisimai::Lhost::Exchange2007
- Defined in:
- lib/sisimai/lhost/exchange2007.rb
Overview
Sisimai::Lhost::Exchange2007 decodes a bounce email which created by Microsoft Exchange Server 2007 www.microsoft.com/microsoft-365/exchange/email. Methods in the module are called from only Sisimai::Message.
Constant Summary collapse
- Indicators =
Sisimai::Lhost.INDICATORS
- Boundaries =
[ "Original Message Headers", "Original message headers:", # en-US "tes de message d'origine :", # fr-FR/En-têtes de message d'origine "Intestazioni originali del messaggio:", # it-CH "Ursprungshuvuden:", # sv-SE ].freeze
- StartingOf =
{ error: [" RESOLVER.", " QUEUE."], message: [ "Error Details", "Diagnostic information for administrators:", # en-US "Informations de diagnostic pour les administrateurs", # fr-FR "Informazioni di diagnostica per gli amministratori", # it-CH "Diagnostisk information f", # sv-SE ], rhost: [ "DSN generated by:", "Generating server", # en-US "Serveur de g", # fr-FR/Serveur de gènèration "Server di generazione", # it-CH "Genererande server", # sv-SE ] }.freeze
- NDRSubject =
{ "SMTPSEND.DNS.NonExistentDomain" => "hostunknown", # 554 5.4.4 SMTPSEND.DNS.NonExistentDomain "SMTPSEND.DNS.MxLoopback" => "networkerror", # 554 5.4.4 SMTPSEND.DNS.MxLoopback "RESOLVER.ADR.BadPrimary" => "systemerror", # 550 5.2.0 RESOLVER.ADR.BadPrimary "RESOLVER.ADR.RecipNotFound" => "userunknown", # 550 5.1.1 RESOLVER.ADR.RecipNotFound "RESOLVER.ADR.RecipientNotFound" => "userunknown", # 550 5.1.1 RESOLVER.ADR.RecipientNotFound "RESOLVER.ADR.ExRecipNotFound" => "userunknown", # 550 5.1.1 RESOLVER.ADR.ExRecipNotFound "RESOLVER.ADR.RecipLimit" => "ratelimited", # 550 5.5.3 RESOLVER.ADR.RecipLimit "RESOLVER.ADR.InvalidInSmtp" => "systemerror", # 550 5.1.0 RESOLVER.ADR.InvalidInSmtp "RESOLVER.ADR.Ambiguous" => "systemerror", # 550 5.1.4 RESOLVER.ADR.Ambiguous, 420 4.2.0 RESOLVER.ADR.Ambiguous "RESOLVER.RST.AuthRequired" => "securityerror", # 550 5.7.1 RESOLVER.RST.AuthRequired "RESOLVER.RST.NotAuthorized" => "rejected", # 550 5.7.1 RESOLVER.RST.NotAuthorized "RESOLVER.RST.RecipSizeLimit" => "emailtoolarge", # 550 5.2.3 RESOLVER.RST.RecipSizeLimit "QUEUE.Expired" => "expired", # 550 4.4.7 QUEUE.Expired }.freeze
- MailSender =
550 4.4.7 QUEUE.Expired
["postmaster@outlook.com", ".onmicrosoft.com"].freeze
- EmailTitle =
[ # Subject: Content-Language: "Undeliverable", # en-US "Non_remis_", # fr-FR "Non remis ", # fr-FR "Non recapitabile", # it-CH "Olevererbart", # sv-SE ].freeze
Class Method Summary collapse
Class Method Details
.description ⇒ Object
170 |
# File 'lib/sisimai/lhost/exchange2007.rb', line 170 def description; return 'Microsoft Exchange Server 2007'; end |
.inquire(mhead, mbody) ⇒ Hash, Nil
This method is abstract.
Decodes the bounce message from Microsoft Exchange Server 2007
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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/sisimai/lhost/exchange2007.rb', line 64 def inquire(mhead, mbody) proceedsto = 0 proceedsto += 1 if EmailTitle.any? { |a| mhead["subject"].include?(a) } proceedsto += 1 if MailSender.any? { |a| mhead["from"].include?(a) } proceedsto += 1 if StartingOf[:error].any? { |a| mbody.include?(a) } proceedsto += 1 if StartingOf[:message].any? { |a| mbody.include?(a) } proceedsto += 1 if mhead["content-language"] return nil if proceedsto < 2 require "sisimai/rfc1123" require "sisimai/smtp/reply" require "sisimai/smtp/status" dscontents = [Sisimai::Lhost.DELIVERYSTATUS] emailparts = Sisimai::RFC5322.part(mbody, Boundaries) bodyslices = emailparts[0].split("\n") readcursor = 0 # (Integer) Points the current cursor position recipients = 0 # (Integer) The number of 'Final-Recipient' header 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. if readcursor == 0 # Beginning of the bounce message or delivery status part readcursor |= Indicators[:deliverystatus] if StartingOf[:message].any? { |a| e.start_with?(a) } next end next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty? # Diagnostic information for administrators: # # Generating server: mta2.neko.example.jp # # kijitora@example.jp # #550 5.1.1 RESOLVER.ADR.RecipNotFound; not found ## # # Original message headers: v = dscontents[-1] if e.include?('@') && e.include?(' ') == false # 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"] = Sisimai::Address.s3s4(e) v["diagnosis"] = "" recipients += 1 else # Try to pick the remote hostname and status code, reply code from the error message if StartingOf[:rhost].any? { |a| e.start_with?(a) } # Generating server: SG2APC01HT234.mail.protection.outlook.com # DSN generated by: NEKONYAAN0022.apcprd01.prod.exchangelabs.com cv = Sisimai::RFC1123.find(e) v["rhost"] = cv if Sisimai::RFC1123.is_internethost(cv) else # #550 5.1.1 RESOLVER.ADR.RecipNotFound; not found ## # #550 5.2.3 RESOLVER.RST.RecipSizeLimit; message too large for this recipient ## cr = Sisimai::SMTP::Reply.find(e) || "" cs = Sisimai::SMTP::Status.find(e) || "" if cr != "" || cs != "" || e.include?("Remote Server ") # Remote Server returned '550 5.1.1 RESOLVER.ADR.RecipNotFound; not found' # 3/09/2016 8:05:56 PM - Remote Server at mydomain.com (10.1.1.3) returned '550 4.4.7 QUEUE.Expired; message expired' v["replycode"] = cr v["status"] = cs v["diagnosis"] += "#{e }" end end end end while recipients == 0 # Try to pick the recipient address from the following formatted bounce message: # Original Message Details # Created Date: 4/29/2017 11:23:34 PM # Sender Address: neko@example.com # Recipient Address: kijitora-nyaan@neko.kyoto.example.jp # Subject: Nyaan? p1 = emailparts[0].index("Original Message Details"); break if p1.nil? p2 = emailparts[0].index("\nRecipient Address: "); break if p2.nil? p3 = emailparts[0].index("\n", p2 + 20); break if p3.nil? cv = Sisimai::Address.s3s4(emailparts[0][p2 + 20, p3 - p2 - 20]) break if Sisimai::Address.is_emailaddress(cv) == false dscontents[0]["recipient"] = cv recipients += 1 end return nil if recipients == 0 dscontents.each do |e| p0 = -1; StartingOf[:error].each do |r| # Try to find the NDR subject string such as "RESOLVER.ADR.RecipientNotFound" from the # error message p0 = e["diagnosis"].index(r); break if p0.nil? == false end next if p0.nil? # #550 5.1.1 RESOLVER.ADR.RecipNotFound; not found ## cv = e["diagnosis"][p0 + 1, e["diagnosis"].index(";") - p0 - 1] next if NDRSubject.has_key?(cv) == false e["reason"] = NDRSubject[cv] end return { "ds" => dscontents, "rfc822" => emailparts[1] } end |