|
| 1 | +module Sisimai::Lhost |
| 2 | + # Sisimai::Lhost::DeutscheTelekom decodes a bounce email which created by Deutsche Telekom or |
| 3 | + # T-Online. Methods in the module are called from only Sisimai::Message. |
| 4 | + module DeutscheTelekom |
| 5 | + class << self |
| 6 | + require 'sisimai/lhost' |
| 7 | + |
| 8 | + Indicators = Sisimai::Lhost.INDICATORS |
| 9 | + BannerDTAG = Sisimai::Lhost.BannerDTAG |
| 10 | + StartingOf = { message: [BannerDTAG[1]] }.freeze |
| 11 | + |
| 12 | + # @abstract Decodes the bounce message from DeutscheTelekom |
| 13 | + # @param [Hash] mhead Message headers of a bounce email |
| 14 | + # @param [String] mbody Message body of a bounce email |
| 15 | + # @return [Hash] Bounce data list and message/rfc822 part |
| 16 | + # @return [Nil] it failed to decode or the arguments are missing |
| 17 | + def inquire(mhead, mbody) |
| 18 | + # - T-Online: https://www.t-online.de/, @t-online.de, @magenta.de |
| 19 | + # - DeutscheTelekom: https://www.telekom.com/ |
| 20 | + # - Based on the bounce format of Smail 3, the original design model for Exim |
| 21 | + # - Tailored for Deutsche Telekom's internal Smail 3 fork with custom banners |
| 22 | + # - Module name follows the infrastructure owner for cross-language compatibility |
| 23 | + # - Smail 3: http://www.weird.com/~woods/projects/smail.html |
| 24 | + return nil unless BannerDTAG.any? { |a| mbody.include?(a) } |
| 25 | + |
| 26 | + # smail-3.2.0.108/src/ |
| 27 | + # notify.c:1052|(void) fprintf(f, "Subject: mail failed, %s\nReference: <%s@%s>\n\n", |
| 28 | + # notify.c:1053| subject_to, message_id, primary_name); |
| 29 | + # |
| 30 | + # T-Online specific headers |
| 31 | + # Received: from mailin42.aul.t-online.de (mailin42.aul.t-online.de [192.51.100.1]) |
| 32 | + # by mailout11.t-online.de (Postfix) with SMTP id 05E5A1CAC0 |
| 33 | + # From: Mail Delivery System <Mailer-Daemon@t-online.de> |
| 34 | + # X-TOI-MSGID: c9412855-531f-497b-b007-5ffc033877a0 |
| 35 | + dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil |
| 36 | + emailparts = Sisimai::RFC5322.part(mbody, [BannerDTAG[3], BannerDTAG[2]]) |
| 37 | + bodyslices = emailparts[0].split("\n") |
| 38 | + messagelog = '' |
| 39 | + readcursor = 0 # (Integer) Points the current cursor position |
| 40 | + recipients = 0 # (Integer) The number of 'Final-Recipient' header |
| 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 | + if readcursor == 0 |
| 46 | + # Beginning of the bounce message or delivery status part |
| 47 | + if e.start_with?(StartingOf[:message][0]) |
| 48 | + # |------------------------- Failed addresses follow: ---------------------| |
| 49 | + readcursor |= Indicators[:deliverystatus] |
| 50 | + else |
| 51 | + # |------------------------- Message log follows: -------------------------| |
| 52 | + # The line above may appears only in Smail 3. |
| 53 | + # |
| 54 | + # smail-3.2.0.108/src/ |
| 55 | + # models.c:787| if (deliver == NULL && defer == NULL) { |
| 56 | + # models.c:788| write_log(WRITE_LOG_MLOG, "no valid recipients were found for this message"); |
| 57 | + # models.c:789| return_to_sender = TRUE; |
| 58 | + # models.c:879| } |
| 59 | + messagelog += ' ' + e if e != "" && e.include?(BannerDTAG[0]) == false |
| 60 | + end |
| 61 | + next |
| 62 | + end |
| 63 | + next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty? |
| 64 | + |
| 65 | + # |------------------------- Failed addresses follow: ---------------------| |
| 66 | + # <example@t-online.de> |
| 67 | + # 552 5.2.2 <example@t-online.de> Quota exceeded (mailbox for user is full) |
| 68 | + # |
| 69 | + # |------------------------- Message header follows: ----------------------| |
| 70 | + # Received: from mail.fragdenstaat.de ([94.130.55.89]) by mailin41.mgt.mul.t-online.de.example.com |
| 71 | + # with (TLSv1.3:TLS_AES_256_GCM_SHA384 encrypted) |
| 72 | + # ... |
| 73 | + v = dscontents[-1] |
| 74 | + |
| 75 | + |
| 76 | + if e.start_with?(' <') && e.end_with?('>') && e.count(' ') == 1 |
| 77 | + # Deutsche Telekom: The recipient address is enclosed in angle brackets. |
| 78 | + # |------------------------- Failed addresses follow: ---------------------| |
| 79 | + if v["recipient"] != "" |
| 80 | + # There are multiple recipient addresses in the message body. |
| 81 | + dscontents << Sisimai::Lhost.DELIVERYSTATUS |
| 82 | + v = dscontents[-1] |
| 83 | + end |
| 84 | + v['recipient'] = e[2, e.size] |
| 85 | + recipients += 1 |
| 86 | + |
| 87 | + elsif Sisimai::String.aligned(e, [' ', '@', '.', ' ... ']) |
| 88 | + # Smail 3: |
| 89 | + # - The recipient address is not enclosed in angle brackets. |
| 90 | + # - Error message begins with " ... failed:" |
| 91 | + # smail-3.2.0.108/src/ |
| 92 | + # notify.c:845| if (cur->error) { |
| 93 | + # notify.c:846| (void) fprintf(f, " %s ... failed: %s\n", |
| 94 | + # notify.c:847| cur->in_addr ? cur->in_addr : "(unknown)", |
| 95 | + # notify.c:848| cur->error->message); |
| 96 | + # notify.c:849| } |
| 97 | + # |------------------------- Failed addresses follow: ---------------------| |
| 98 | + # kijitora@neko.nyaan.example.com ... unknown host |
| 99 | + if v["recipient"] != "" |
| 100 | + # There are multiple recipient addresses in the message body. |
| 101 | + dscontents << Sisimai::Lhost.DELIVERYSTATUS |
| 102 | + v = dscontents[-1] |
| 103 | + end |
| 104 | + v['recipient'] = e[1, e.index(' ... ')] |
| 105 | + v['diagnosis'] = messagelog << ' ' + e |
| 106 | + recipients += 1 |
| 107 | + |
| 108 | + else |
| 109 | + # 552 5.2.2 <example@t-online.de> Quota exceeded (mailbox for user is full) |
| 110 | + v['diagnosis'] = e |
| 111 | + end |
| 112 | + end |
| 113 | + return nil if recipients == 0 |
| 114 | + return { 'ds' => dscontents, 'rfc822' => emailparts[1] } |
| 115 | + end |
| 116 | + def description; return 'DeutscheTelekom'; end |
| 117 | + end |
| 118 | + end |
| 119 | +end |
| 120 | + |
0 commit comments