sisimai 5.0.0-java → 5.0.3-java
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.
- checksums.yaml +4 -4
- data/.github/workflows/codecovio.yml +33 -0
- data/.github/workflows/rake-test.yml +54 -0
- data/ChangeLog.md +68 -1
- data/Gemfile +2 -0
- data/README-JA.md +224 -112
- data/README.md +56 -33
- data/lib/sisimai/arf.rb +24 -5
- data/lib/sisimai/fact.rb +46 -8
- data/lib/sisimai/lhost/amazonses.rb +0 -1
- data/lib/sisimai/lhost/amazonworkmail.rb +0 -1
- data/lib/sisimai/lhost/aol.rb +0 -1
- data/lib/sisimai/lhost/bigfoot.rb +0 -1
- data/lib/sisimai/lhost/domino.rb +0 -1
- data/lib/sisimai/lhost/exchange2007.rb +1 -1
- data/lib/sisimai/lhost/exim.rb +7 -16
- data/lib/sisimai/lhost/facebook.rb +0 -1
- data/lib/sisimai/lhost/googlegroups.rb +2 -1
- data/lib/sisimai/lhost/gsuite.rb +0 -1
- data/lib/sisimai/lhost/mailmarshalsmtp.rb +1 -1
- data/lib/sisimai/lhost/mailru.rb +8 -17
- data/lib/sisimai/lhost/messagelabs.rb +0 -1
- data/lib/sisimai/lhost/mfilter.rb +1 -1
- data/lib/sisimai/lhost/mxlogic.rb +8 -18
- data/lib/sisimai/lhost/office365.rb +1 -1
- data/lib/sisimai/lhost/outlook.rb +0 -1
- data/lib/sisimai/lhost/postfix.rb +0 -1
- data/lib/sisimai/lhost/receivingses.rb +0 -1
- data/lib/sisimai/lhost/sendgrid.rb +1 -3
- data/lib/sisimai/lhost/sendmail.rb +0 -1
- data/lib/sisimai/lhost/yandex.rb +0 -1
- data/lib/sisimai/message.rb +14 -5
- data/lib/sisimai/reason/authfailure.rb +1 -0
- data/lib/sisimai/reason/blocked.rb +3 -0
- data/lib/sisimai/reason/expired.rb +8 -0
- data/lib/sisimai/reason/filtered.rb +1 -0
- data/lib/sisimai/reason/mailboxfull.rb +4 -0
- data/lib/sisimai/reason/norelaying.rb +1 -0
- data/lib/sisimai/reason/rejected.rb +1 -1
- data/lib/sisimai/reason/securityerror.rb +1 -0
- data/lib/sisimai/reason/spamdetected.rb +1 -0
- data/lib/sisimai/reason/suspend.rb +5 -0
- data/lib/sisimai/reason/userunknown.rb +4 -1
- data/lib/sisimai/rfc5322.rb +120 -64
- data/lib/sisimai/rhost/google.rb +347 -71
- data/lib/sisimai/rhost/microsoft.rb +8 -0
- data/lib/sisimai/rhost/mimecast.rb +9 -2
- data/lib/sisimai/rhost/nttdocomo.rb +3 -3
- data/lib/sisimai/smtp/status.rb +3 -0
- data/lib/sisimai/version.rb +1 -1
- data/lib/sisimai.rb +12 -11
- data/set-of-emails/maildir/bsd/arf-26.eml +27 -0
- data/set-of-emails/maildir/bsd/lhost-sendmail-60.eml +85 -0
- data/set-of-emails/maildir/bsd/rhost-microsoft-04.eml +86 -0
- data/set-of-emails/maildir/bsd/rhost-microsoft-05.eml +83 -0
- metadata +13 -8
- data/.travis.yml +0 -23
| @@ -168,7 +168,6 @@ module Sisimai::Lhost | |
| 168 168 | 
             
                    dscontents.each do |e|
         | 
| 169 169 | 
             
                      # Set default values if each value is empty.
         | 
| 170 170 | 
             
                      e['diagnosis'] ||= ''
         | 
| 171 | 
            -
                      e['lhost']     ||= permessage['rhost']
         | 
| 172 171 | 
             
                      permessage.each_key { |a| e[a] ||= permessage[a] || '' }
         | 
| 173 172 |  | 
| 174 173 | 
             
                      if anotherset['diagnosis']
         | 
    
        data/lib/sisimai/lhost/yandex.rb
    CHANGED
    
    | @@ -103,7 +103,6 @@ module Sisimai::Lhost | |
| 103 103 |  | 
| 104 104 | 
             
                    dscontents.each do |e|
         | 
| 105 105 | 
             
                      # Set default values if each value is empty.
         | 
| 106 | 
            -
                      e['lhost'] ||= permessage['rhost']
         | 
| 107 106 | 
             
                      permessage.each_key { |a| e[a] ||= permessage[a] || '' }
         | 
| 108 107 |  | 
| 109 108 | 
             
                      e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].tr("\n", ' '))
         | 
    
        data/lib/sisimai/message.rb
    CHANGED
    
    | @@ -198,17 +198,26 @@ module Sisimai | |
| 198 198 | 
             
                    # Select and convert all the headers in $argv0. The following regular expression is based on
         | 
| 199 199 | 
             
                    # https://gist.github.com/xtetsuji/b080e1f5551d17242f6415aba8a00239
         | 
| 200 200 | 
             
                    headermaps = { 'subject' => '' }
         | 
| 201 | 
            -
                     | 
| 201 | 
            +
                    receivedby = []
         | 
| 202 202 | 
             
                    argv0.scan(/^([\w-]+):[ ]*(.*?)\n(?![\s\t])/m) { |e| headermaps[e[0].downcase] = e[1] }
         | 
| 203 203 | 
             
                    headermaps.delete('received')
         | 
| 204 204 | 
             
                    headermaps.each_key { |e| headermaps[e].gsub!(/\n[\s\t]+/, ' ') }
         | 
| 205 205 |  | 
| 206 206 | 
             
                    if argv0.include?('Received:')
         | 
| 207 207 | 
             
                      # Capture values of each Received: header
         | 
| 208 | 
            -
                       | 
| 209 | 
            -
                       | 
| 208 | 
            +
                      re = argv0.scan(/^Received:[ ]*(.*?)\n(?![\s\t])/m).flatten
         | 
| 209 | 
            +
                      re.each do |e|
         | 
| 210 | 
            +
                        # 1. Exclude the Received header including "(qmail ** invoked from network)".
         | 
| 211 | 
            +
                        # 2. Convert all consecutive spaces and line breaks into a single space character.
         | 
| 212 | 
            +
                        next if e.include?(' invoked by uid')
         | 
| 213 | 
            +
                        next if e.include?(' invoked from network')
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                        e.gsub!(/\n[\s\t]+/, ' ')
         | 
| 216 | 
            +
                        e.squeeze!("\n\t ")
         | 
| 217 | 
            +
                        receivedby << e
         | 
| 218 | 
            +
                      end
         | 
| 210 219 | 
             
                    end
         | 
| 211 | 
            -
                    headermaps['received'] =  | 
| 220 | 
            +
                    headermaps['received'] = receivedby
         | 
| 212 221 |  | 
| 213 222 | 
             
                    return headermaps unless argv1
         | 
| 214 223 | 
             
                    return headermaps if headermaps['subject'].empty?
         | 
| @@ -443,7 +452,7 @@ module Sisimai | |
| 443 452 | 
             
                        unless haveloaded['Sisimai::ARF']
         | 
| 444 453 | 
             
                          # Feedback Loop message
         | 
| 445 454 | 
             
                          require 'sisimai/arf'
         | 
| 446 | 
            -
                          havesifted = Sisimai::ARF.inquire(mailheader, bodystring) | 
| 455 | 
            +
                          havesifted = Sisimai::ARF.inquire(mailheader, bodystring)
         | 
| 447 456 | 
             
                          throw :PARSER if havesifted
         | 
| 448 457 | 
             
                        end
         | 
| 449 458 |  | 
| @@ -46,6 +46,7 @@ module Sisimai | |
| 46 46 | 
             
                      'part of their network is on our block list',
         | 
| 47 47 | 
             
                      'please use the smtp server of your isp',
         | 
| 48 48 | 
             
                      'refused - see http',
         | 
| 49 | 
            +
                      'rejected - multi-blacklist', # junkemailfilter.com
         | 
| 49 50 | 
             
                      'rejected because the sending mta or the sender has not passed validation',
         | 
| 50 51 | 
             
                      'rejecting open proxy', # Sendmail(srvrsmtp.c)
         | 
| 51 52 | 
             
                      'sender ip address rejected',
         | 
| @@ -61,6 +62,7 @@ module Sisimai | |
| 61 62 | 
             
                      'we do not accept mail from dynamic ips',   # @mail.ru
         | 
| 62 63 | 
             
                      'you are not allowed to connect',
         | 
| 63 64 | 
             
                      'you are sending spam',
         | 
| 65 | 
            +
                      'your ip address is listed in the rbl',
         | 
| 64 66 | 
             
                      'your network is temporary blacklisted',
         | 
| 65 67 | 
             
                      'your server requires confirmation',
         | 
| 66 68 | 
             
                    ].freeze
         | 
| @@ -78,6 +80,7 @@ module Sisimai | |
| 78 80 | 
             
                      ['message from ', ' rejected based on blacklist'],
         | 
| 79 81 | 
             
                      ['messages from ', ' temporarily deferred due to user complaints'], # Yahoo!
         | 
| 80 82 | 
             
                      ['server ip ', ' listed as abusive'],
         | 
| 83 | 
            +
                      ['sorry! your ip address', ' is blocked by rbl'], # junkemailfilter.com
         | 
| 81 84 | 
             
                      ['the domain ', ' is blacklisted'],
         | 
| 82 85 | 
             
                      ['the email ', ' is blacklisted'],
         | 
| 83 86 | 
             
                      ['the ip', ' is blacklisted'],
         | 
| @@ -7,10 +7,13 @@ module Sisimai | |
| 7 7 | 
             
                # the message you sent has been in the queue for long time.
         | 
| 8 8 | 
             
                module Expired
         | 
| 9 9 | 
             
                  class << self
         | 
| 10 | 
            +
                    require 'sisimai/string'
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
                    Index = [
         | 
| 11 13 | 
             
                      'connection timed out',
         | 
| 12 14 | 
             
                      'could not find a gateway for',
         | 
| 13 15 | 
             
                      'delivery attempts will continue to be',
         | 
| 16 | 
            +
                      'delivery expired',
         | 
| 14 17 | 
             
                      'delivery time expired',
         | 
| 15 18 | 
             
                      'failed to deliver to domain ',
         | 
| 16 19 | 
             
                      'giving up on',
         | 
| @@ -18,6 +21,7 @@ module Sisimai | |
| 18 21 | 
             
                      'has been delayed',
         | 
| 19 22 | 
             
                      'it has not been collected after',
         | 
| 20 23 | 
             
                      'message expired after sitting in queue for',
         | 
| 24 | 
            +
                      'message expired, cannot connect to remote server',
         | 
| 21 25 | 
             
                      'message expired, connection refulsed',
         | 
| 22 26 | 
             
                      'message timed out',
         | 
| 23 27 | 
             
                      'retry time not reached for any host after a long failure period',
         | 
| @@ -27,6 +31,9 @@ module Sisimai | |
| 27 31 | 
             
                      'was not reachable within the allowed queue period',
         | 
| 28 32 | 
             
                      'your message could not be delivered for more than',
         | 
| 29 33 | 
             
                    ].freeze
         | 
| 34 | 
            +
                    Pairs = [
         | 
| 35 | 
            +
                      ['could not be delivered for', ' days'],
         | 
| 36 | 
            +
                    ].freeze
         | 
| 30 37 |  | 
| 31 38 | 
             
                    def text; return 'expired'; end
         | 
| 32 39 | 
             
                    def description; return 'Delivery time has expired due to a connection failure'; end
         | 
| @@ -38,6 +45,7 @@ module Sisimai | |
| 38 45 | 
             
                    def match(argv1)
         | 
| 39 46 | 
             
                      return nil unless argv1
         | 
| 40 47 | 
             
                      return true if Index.any? { |a| argv1.include?(a) }
         | 
| 48 | 
            +
                      return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
         | 
| 41 49 | 
             
                      return false
         | 
| 42 50 | 
             
                    end
         | 
| 43 51 |  | 
| @@ -21,6 +21,7 @@ module Sisimai | |
| 21 21 | 
             
                      'resolver.rst.notauthorized',   # Microsoft Exchange
         | 
| 22 22 | 
             
                      'this account is protected by',
         | 
| 23 23 | 
             
                      'user not found',   # Filter on MAIL.RU
         | 
| 24 | 
            +
                      'user refuses to receive this mail',
         | 
| 24 25 | 
             
                      'user reject',
         | 
| 25 26 | 
             
                      'we failed to deliver mail because the following address recipient id refuse to receive mail',  # Willcom
         | 
| 26 27 | 
             
                      'you have been blocked by the recipient',
         | 
| @@ -8,6 +8,7 @@ module Sisimai | |
| 8 8 | 
             
                module MailboxFull
         | 
| 9 9 | 
             
                  class << self
         | 
| 10 10 | 
             
                    Index = [
         | 
| 11 | 
            +
                      '452 insufficient disk space',
         | 
| 11 12 | 
             
                      'account disabled temporarly for exceeding receiving limits',
         | 
| 12 13 | 
             
                      'account is exceeding their quota',
         | 
| 13 14 | 
             
                      'account is over quota',
         | 
| @@ -15,6 +16,7 @@ module Sisimai | |
| 15 16 | 
             
                      'boite du destinataire pleine',
         | 
| 16 17 | 
             
                      'delivery failed: over quota',
         | 
| 17 18 | 
             
                      'disc quota exceeded',
         | 
| 19 | 
            +
                      'diskspace quota',
         | 
| 18 20 | 
             
                      'does not have enough space',
         | 
| 19 21 | 
             
                      'exceeded storage allocation',
         | 
| 20 22 | 
             
                      'exceeding its mailbox quota',
         | 
| @@ -34,7 +36,9 @@ module Sisimai | |
| 34 36 | 
             
                      'maildir delivery failed: userdisk quota ',
         | 
| 35 37 | 
             
                      'maildir delivery failed: domaindisk quota ',
         | 
| 36 38 | 
             
                      'mailfolder is full',
         | 
| 39 | 
            +
                      'no space left on device',
         | 
| 37 40 | 
             
                      'not enough storage space in',
         | 
| 41 | 
            +
                      'not sufficient disk space',
         | 
| 38 42 | 
             
                      'over the allowed quota',
         | 
| 39 43 | 
             
                      'quota exceeded',
         | 
| 40 44 | 
             
                      'quota violation for',
         | 
| @@ -24,6 +24,7 @@ module Sisimai | |
| 24 24 | 
             
                      'relay not permitted',
         | 
| 25 25 | 
             
                      'relaying denied',  # Sendmail
         | 
| 26 26 | 
             
                      'relaying mail to ',
         | 
| 27 | 
            +
                      'specified domain is not allowed',
         | 
| 27 28 | 
             
                      "that domain isn't in my list of allowed rcpthost",
         | 
| 28 29 | 
             
                      'this system is not configured to relay mail',
         | 
| 29 30 | 
             
                      'unable to relay for',
         | 
| @@ -38,7 +38,7 @@ module Sisimai | |
| 38 38 | 
             
                      'email address is on senderfilterconfig list',
         | 
| 39 39 | 
             
                      'emetteur invalide',
         | 
| 40 40 | 
             
                      'empty envelope senders not allowed',
         | 
| 41 | 
            -
                      'envelope blocked  | 
| 41 | 
            +
                      'envelope blocked - ',
         | 
| 42 42 | 
             
                      'error: no third-party dsns',   # SpamWall - block empty sender
         | 
| 43 43 | 
             
                      'from: domain is invalid. please provide a valid from:',
         | 
| 44 44 | 
             
                      'fully qualified email address required',   # McAfee
         | 
| @@ -29,6 +29,7 @@ module Sisimai | |
| 29 29 | 
             
                      'insecure mail relay',
         | 
| 30 30 | 
             
                      'recipient address rejected: access denied',
         | 
| 31 31 | 
             
                      "sorry, you don't authenticate or the domain isn't in my list of allowed rcpthosts",
         | 
| 32 | 
            +
                      'starttls is required to send mail',
         | 
| 32 33 | 
             
                      'tls required but not supported',   # SendGrid:the recipient mailserver does not support TLS or have a valid certificate
         | 
| 33 34 | 
             
                      'unauthenticated senders not allowed',
         | 
| 34 35 | 
             
                      'verification failure',
         | 
| @@ -76,6 +76,7 @@ module Sisimai | |
| 76 76 | 
             
                      'spam score ',
         | 
| 77 77 | 
             
                      'spambouncer identified spam',  # SpamBouncer identified SPAM
         | 
| 78 78 | 
             
                      'spamming not allowed',
         | 
| 79 | 
            +
                      'too many spam complaints',
         | 
| 79 80 | 
             
                      'too much spam.',               # Earthlink
         | 
| 80 81 | 
             
                      'the email message was detected as spam',
         | 
| 81 82 | 
             
                      'the message has been rejected by spam filtering engine',
         | 
| @@ -12,16 +12,21 @@ module Sisimai | |
| 12 12 | 
             
                      'boite du destinataire archivee',
         | 
| 13 13 | 
             
                      'email account that you tried to reach is disabled',
         | 
| 14 14 | 
             
                      'has been suspended',
         | 
| 15 | 
            +
                      'inactive account',
         | 
| 15 16 | 
             
                      'invalid/inactive user',
         | 
| 16 17 | 
             
                      'is a deactivated mailbox', # http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=20022&&no=1000742
         | 
| 17 18 | 
             
                      'is unavailable: user is terminated',
         | 
| 18 19 | 
             
                      'mailbox currently suspended',
         | 
| 20 | 
            +
                      'mailbox disabled',
         | 
| 19 21 | 
             
                      'mailbox is frozen',
         | 
| 20 22 | 
             
                      'mailbox unavailable or access denied',
         | 
| 21 23 | 
             
                      'recipient rejected: temporarily inactive',
         | 
| 22 24 | 
             
                      'recipient suspend the service',
         | 
| 23 25 | 
             
                      'this account has been disabled or discontinued',
         | 
| 26 | 
            +
                      'this account has been temporarily suspended',
         | 
| 27 | 
            +
                      'this address no longer accepts mail',
         | 
| 24 28 | 
             
                      'this mailbox is disabled',
         | 
| 29 | 
            +
                      'user or domain is disabled',
         | 
| 25 30 | 
             
                      'user suspended',   # http://mail.163.com/help/help_spam_16.htm
         | 
| 26 31 | 
             
                      'vdelivermail: account is locked email bounced',
         | 
| 27 32 | 
             
                    ].freeze
         | 
| @@ -63,7 +63,6 @@ module Sisimai | |
| 63 63 | 
             
                      'no such mailbox',
         | 
| 64 64 | 
             
                      'no such person at this address',
         | 
| 65 65 | 
             
                      'no such recipient',
         | 
| 66 | 
            -
             | 
| 67 66 | 
             
                      'no such user',
         | 
| 68 67 | 
             
                      'no thank you rejected: account unavailable',
         | 
| 69 68 | 
             
                      'no valid recipients, bye',
         | 
| @@ -76,7 +75,9 @@ module Sisimai | |
| 76 75 | 
             
                      'recipient address rejected: invalid user',
         | 
| 77 76 | 
             
                      'recipient address rejected: invalid-recipient',
         | 
| 78 77 | 
             
                      'recipient address rejected: unknown user',
         | 
| 78 | 
            +
                      'recipient address rejected: userunknown',
         | 
| 79 79 | 
             
                      'recipient does not exist',
         | 
| 80 | 
            +
                      'recipient is not accepted',
         | 
| 80 81 | 
             
                      'recipient is not local',
         | 
| 81 82 | 
             
                      'recipient not exist',
         | 
| 82 83 | 
             
                      'recipient not found',
         | 
| @@ -125,6 +126,7 @@ module Sisimai | |
| 125 126 | 
             
                      ['no ', ' in name directory'],
         | 
| 126 127 | 
             
                      ['non', 'existent user'],
         | 
| 127 128 | 
             
                      ['rcpt <', ' does not exist'],
         | 
| 129 | 
            +
                      ['rcpt (', 't exist '],
         | 
| 128 130 | 
             
                      ['recipient ', ' was not found in'],
         | 
| 129 131 | 
             
                      ['recipient address rejected: user ', '  does not exist'],
         | 
| 130 132 | 
             
                      ['recipient address rejected: user unknown in ', '  table'],
         | 
| @@ -135,6 +137,7 @@ module Sisimai | |
| 135 137 | 
             
                      ['unknown local', 'part'],
         | 
| 136 138 | 
             
                      ['user ', ' was not found'],
         | 
| 137 139 | 
             
                      ['user ', ' does not exist'],
         | 
| 140 | 
            +
                      ['user (', ') unknown'],
         | 
| 138 141 | 
             
                    ].freeze
         | 
| 139 142 |  | 
| 140 143 | 
             
                    def text; return 'userunknown'; end
         | 
    
        data/lib/sisimai/rfc5322.rb
    CHANGED
    
    | @@ -3,6 +3,7 @@ module Sisimai | |
| 3 3 | 
             
              module RFC5322
         | 
| 4 4 | 
             
                class << self
         | 
| 5 5 | 
             
                  require 'sisimai/string'
         | 
| 6 | 
            +
                  require 'sisimai/address'
         | 
| 6 7 | 
             
                  HeaderTable = {
         | 
| 7 8 | 
             
                    :messageid => %w[message-id],
         | 
| 8 9 | 
             
                    :subject   => %w[subject],
         | 
| @@ -52,79 +53,134 @@ module Sisimai | |
| 52 53 | 
             
                  # @return   [Array]         Received header as a structured data
         | 
| 53 54 | 
             
                  def received(argv1)
         | 
| 54 55 | 
             
                    return [] unless argv1.is_a?(::String)
         | 
| 56 | 
            +
                    return [] if argv1.include?(' invoked by uid')
         | 
| 57 | 
            +
                    return [] if argv1.include?(' invoked from network')
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    # - https://datatracker.ietf.org/doc/html/rfc5322
         | 
| 60 | 
            +
                    #   received        =   "Received:" *received-token ";" date-time CRLF
         | 
| 61 | 
            +
                    #   received-token  =   word / angle-addr / addr-spec / domain
         | 
| 62 | 
            +
                    #
         | 
| 63 | 
            +
                    # - Appendix A.4. Message with Trace Fields
         | 
| 64 | 
            +
                    #   Received:
         | 
| 65 | 
            +
                    #       from x.y.test
         | 
| 66 | 
            +
                    #       by example.net
         | 
| 67 | 
            +
                    #       via TCP
         | 
| 68 | 
            +
                    #       with ESMTP
         | 
| 69 | 
            +
                    #       id ABC12345
         | 
| 70 | 
            +
                    #       for <mary@example.net>;  21 Nov 1997 10:05:43 -0600
         | 
| 71 | 
            +
                    recvd = argv1.split(' ')
         | 
| 72 | 
            +
                    label = %w[from by via with id for]
         | 
| 73 | 
            +
                    token = {}
         | 
| 74 | 
            +
                    other = []
         | 
| 75 | 
            +
                    alter = []
         | 
| 76 | 
            +
                    right = false
         | 
| 77 | 
            +
                    range = recvd.size
         | 
| 78 | 
            +
                    index = -1
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    recvd.each do |e|
         | 
| 81 | 
            +
                      # Look up each label defined in "label" from Received header
         | 
| 82 | 
            +
                      index += 1
         | 
| 83 | 
            +
                      break unless index < range; f = e.downcase
         | 
| 84 | 
            +
                      next  unless label.any? { |a| f == a }
         | 
| 85 | 
            +
                      token[f] = recvd[index + 1] || next
         | 
| 86 | 
            +
                      token[f] = token[f].downcase.delete('();')
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                      next  unless f == 'from'
         | 
| 89 | 
            +
                      break unless index + 2 < range
         | 
| 90 | 
            +
                      next  unless recvd[index + 2].start_with?('(')
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                      # Get and keep a hostname in the comment as follows:
         | 
| 93 | 
            +
                      # from mx1.example.com (c213502.kyoto.example.ne.jp [192.0.2.135]) by mx.example.jp (V8/cf)
         | 
| 94 | 
            +
                      # [
         | 
| 95 | 
            +
                      #   "from",                         # index + 0
         | 
| 96 | 
            +
                      #   "mx1.example.com",              # index + 1
         | 
| 97 | 
            +
                      #   "(c213502.kyoto.example.ne.jp", # index + 2
         | 
| 98 | 
            +
                      #   "[192.0.2.135])",               # index + 3
         | 
| 99 | 
            +
                      #   "by",
         | 
| 100 | 
            +
                      #   "mx.example.jp",
         | 
| 101 | 
            +
                      #   "(V8/cf)",
         | 
| 102 | 
            +
                      #   ...
         | 
| 103 | 
            +
                      # ]
         | 
| 104 | 
            +
                      # The 2nd element after the current element is NOT a continuation of the current element
         | 
| 105 | 
            +
                      # such as "(c213502.kyoto.example.ne.jp)"
         | 
| 106 | 
            +
                      other << recvd[index + 2].delete('();')
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                      # The 2nd element after the current element is a continuation of the current element.
         | 
| 109 | 
            +
                      # such as "(c213502.kyoto.example.ne.jp", "[192.0.2.135])"
         | 
| 110 | 
            +
                      break unless index + 3 < range
         | 
| 111 | 
            +
                      other << recvd[index + 3].delete('();')
         | 
| 112 | 
            +
                    end
         | 
| 55 113 |  | 
| 56 | 
            -
                     | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
                      #   Wed, 26 Feb 2014 06:05:48 -0500
         | 
| 69 | 
            -
                      value['from'] = Sisimai::String.sweep(argv1[p1 + 5, p2 - p1 - 5])
         | 
| 70 | 
            -
                      value['by']   = Sisimai::String.sweep(argv1[p2 + 3, p3 - p2 - 3])
         | 
| 114 | 
            +
                    other.each do |e|
         | 
| 115 | 
            +
                      # Check alternatives in "other", and then delete uninformative values.
         | 
| 116 | 
            +
                      next if e.nil?
         | 
| 117 | 
            +
                      next if e.size < 4
         | 
| 118 | 
            +
                      next if e == 'unknown'
         | 
| 119 | 
            +
                      next if e == 'localhost'
         | 
| 120 | 
            +
                      next if e == '[127.0.0.1]'
         | 
| 121 | 
            +
                      next if e == '[IPv6:::1]'
         | 
| 122 | 
            +
                      next unless e.include?('.')
         | 
| 123 | 
            +
                      next if e.include?('=')
         | 
| 124 | 
            +
                      alter << e
         | 
| 125 | 
            +
                    end
         | 
| 71 126 |  | 
| 72 | 
            -
                     | 
| 73 | 
            -
                      #  | 
| 74 | 
            -
                       | 
| 75 | 
            -
                       | 
| 127 | 
            +
                    %w[from by].each do |e|
         | 
| 128 | 
            +
                      # Remove square brackets from the IP address such as "[192.0.2.25]"
         | 
| 129 | 
            +
                      next if token[e].nil?
         | 
| 130 | 
            +
                      next if token[e].empty?
         | 
| 131 | 
            +
                      next unless token[e].start_with?('[')
         | 
| 132 | 
            +
                      token[e] = Sisimai::String.ipv4(token[e]).shift || ''
         | 
| 76 133 | 
             
                    end
         | 
| 134 | 
            +
                    token['from'] ||= ''
         | 
| 77 135 |  | 
| 78 | 
            -
                     | 
| 79 | 
            -
                      #  | 
| 80 | 
            -
                       | 
| 81 | 
            -
                       | 
| 82 | 
            -
                       | 
| 83 | 
            -
                       | 
| 84 | 
            -
                      hostname = ''
         | 
| 85 | 
            -
                      hostaddr = ''
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                      while e = received.shift do
         | 
| 88 | 
            -
                        # Received: from [10.22.22.222] (smtp-gateway.kyoto.ocn.ne.jp [192.0.2.222])
         | 
| 89 | 
            -
                        cv = Sisimai::String.ipv4(e) || []
         | 
| 90 | 
            -
                        if cv.size > 0
         | 
| 91 | 
            -
                          # [192.0.2.1] or (192.0.2.1)
         | 
| 92 | 
            -
                          addrlist.append(*cv)
         | 
| 93 | 
            -
                        else
         | 
| 94 | 
            -
                          # hostname
         | 
| 95 | 
            -
                          e = e.delete('()').strip
         | 
| 96 | 
            -
                          namelist << e
         | 
| 97 | 
            -
                        end
         | 
| 98 | 
            -
                      end
         | 
| 136 | 
            +
                    while true do
         | 
| 137 | 
            +
                      # Prefer hostnames over IP addresses, except for localhost.localdomain and similar.
         | 
| 138 | 
            +
                      break if token['from'] == 'localhost'
         | 
| 139 | 
            +
                      break if token['from'] == 'localhost.localdomain'
         | 
| 140 | 
            +
                      break unless token['from'].include?('.')  # A hostname without a domain name
         | 
| 141 | 
            +
                      break unless Sisimai::String.ipv4(token['from']).empty?
         | 
| 99 142 |  | 
| 100 | 
            -
                       | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
                        break
         | 
| 105 | 
            -
                      end
         | 
| 143 | 
            +
                      # No need to rewrite token['from']
         | 
| 144 | 
            +
                      right = true
         | 
| 145 | 
            +
                      break
         | 
| 146 | 
            +
                    end
         | 
| 106 147 |  | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
                         | 
| 148 | 
            +
                    while true do
         | 
| 149 | 
            +
                      # Try to rewrite uninformative hostnames and IP addresses in token['from']
         | 
| 150 | 
            +
                      break if right        # There is no need to rewrite
         | 
| 151 | 
            +
                      break if alter.empty? # There is no alternative to rewriting
         | 
| 152 | 
            +
                      break if alter[0].include?(token['from'])
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                      if token['from'].start_with?('localhost')
         | 
| 155 | 
            +
                        # localhost or localhost.localdomain
         | 
| 156 | 
            +
                        token['from'] = alter[0]
         | 
| 157 | 
            +
                      elsif token['from'].index('.')
         | 
| 158 | 
            +
                        # A hostname without a domain name such as "mail", "mx", or "mbox"
         | 
| 159 | 
            +
                        token['from'] = alter[0] if alter[0].include?('.')
         | 
| 160 | 
            +
                      else
         | 
| 161 | 
            +
                        # An IPv4 address
         | 
| 162 | 
            +
                        token['from'] = alter[0]
         | 
| 116 163 | 
             
                      end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                      value['from'] = hostname || hostaddr || addrlist[-1]
         | 
| 164 | 
            +
                      break
         | 
| 119 165 | 
             
                    end
         | 
| 120 | 
            -
             | 
| 121 | 
            -
                     | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
                       | 
| 166 | 
            +
                    token.delete('from') if token['from'].nil?
         | 
| 167 | 
            +
                    token.delete('by')   if token['by'].nil?
         | 
| 168 | 
            +
                    token['for'] = Sisimai::Address.s3s4(token['for']) if token.has_key?('for')
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                    token.keys.each do |e|
         | 
| 171 | 
            +
                      # Delete an invalid value
         | 
| 172 | 
            +
                      token[e] = '' if token[e].include?(' ')
         | 
| 173 | 
            +
                      token[e].delete!('[]')  # Remove "[]" from the IP address
         | 
| 126 174 | 
             
                    end
         | 
| 127 | 
            -
             | 
| 175 | 
            +
             | 
| 176 | 
            +
                    return [
         | 
| 177 | 
            +
                      token['from'] || '',
         | 
| 178 | 
            +
                      token['by']   || '',
         | 
| 179 | 
            +
                      token['via']  || '',
         | 
| 180 | 
            +
                      token['with'] || '',
         | 
| 181 | 
            +
                      token['id']   || '',
         | 
| 182 | 
            +
                      token['for']  || '',
         | 
| 183 | 
            +
                    ]
         | 
| 128 184 | 
             
                  end
         | 
| 129 185 |  | 
| 130 186 | 
             
                  # Split given entire message body into error message lines and the original message part only
         |