Module: Sisimai::SMTP::Status
- Defined in:
- lib/sisimai/smtp/status.rb
Overview
Sisimai::RFC3463 is utilities for getting D.S.N. value from error reason text, getting the reason from D.S.N. value, and getting D.S.N. from the text including D.S.N.
Constant Summary collapse
- CodePatterns =
[ %r/[ ]?[(][#]([45][.]\d[.]\d+)[)]?[ ]?/, # #5.5.1 %r/\b\d{3}[- ][#]?([45][.]\d[.]\d+)\b/, # 550-5.1.1 OR 550 5.5.1 %r/\b([45][.]\d[.]\d+)\b/, # 5.5.1 %r/\b(2[.][0-7][.][0-7])\b/, # 2.1.5 ]
- StandardCode =
{ '2.1.5' => 'delivered', # Successfully delivered # --------------------------------------------------------------------------------------- '4.1.6' => 'hasmoved', # Destination mailbox has moved, No forwarding address '4.1.7' => 'rejected', # Bad sender's mailbox address syntax '4.1.8' => 'rejected', # Bad sender's system address '4.1.9' => 'systemerror', # Message relayed to non-compliant mailer '4.2.1' => 'suspend', # Mailbox disabled, not accepting messages '4.2.2' => 'mailboxfull', # Mailbox full '4.2.3' => 'emailtoolarge', # Message length exceeds administrative limit '4.2.4' => 'systemerror', # Mailing list expansion problem # '4.3.0' => 'systemerror', # Other or undefined mail system status '4.3.1' => 'systemfull', # Mail system full '4.3.2' => 'notaccept', # System not accepting network messages '4.3.3' => 'systemerror', # System not capable of selected features '4.3.5' => 'systemerror', # System incorrectly configured # '4.4.0' => 'networkerror', # Other or undefined network or routing status '4.4.1' => 'expired', # No answer from host '4.4.2' => 'networkerror', # Bad connection '4.4.3' => 'systemerror', # Directory server failure '4.4.4' => 'networkerror', # Unable to route '4.4.5' => 'systemfull', # Mail system congestion '4.4.6' => 'networkerror', # Routing loop detected '4.4.7' => 'expired', # Delivery time expired '4.4.8' => 'networkerror', # Retry on IPv4 # '4.5.0' => 'networkerror', # Other or undefined protocol status '4.5.3' => 'ratelimited', # Too many recipients '4.5.5' => 'systemerror', # Wrong protocol version '4.6.0' => 'contenterror', # Other or undefined media error '4.6.2' => 'contenterror', # Conversion required and prohibited '4.6.5' => 'contenterror', # Conversion Failed # :'4.7.0' => 'securityerror', # Other or undefined security status '4.7.1' => 'blocked', # Delivery not authorized, message refused '4.7.2' => 'rejected', # Mailing list expansion prohibited '4.7.5' => 'securityerror', # Cryptographic failure '4.7.6' => 'securityerror', # Cryptographic algorithm not supported '4.7.7' => 'securityerror', # Message integrity failure '4.7.12' => 'securityerror', # A password transition is needed '4.7.15' => 'securityerror', # Priority Level is too low '4.7.16' => 'emailtoolarge', # Message is too big for the specified priority '4.7.24' => 'authfailure ', # SPF validation error '4.7.25' => 'requireptr', # Reverse DNS validation failed # --------------------------------------------------------------------------------------- '5.1.0' => 'userunknown', # Other address status '5.1.1' => 'userunknown', # Bad destination mailbox address '5.1.2' => 'hostunknown', # Bad destination system address '5.1.3' => 'userunknown', # Bad destination mailbox address syntax '5.1.4' => 'filtered', # Destination mailbox address ambiguous '5.1.6' => 'hasmoved', # Destination mailbox has moved, No forwarding address '5.1.7' => 'rejected', # Bad sender's mailbox address syntax '5.1.8' => 'rejected', # Bad sender's system address '5.1.9' => 'systemerror', # Message relayed to non-compliant mailer '5.1.10' => 'notaccept', # Recipient address has null MX '5.2.0' => 'filtered', # Other or undefined mailbox status '5.2.1' => 'filtered', # Mailbox disabled, not accepting messages '5.2.2' => 'mailboxfull', # Mailbox full '5.2.3' => 'emailtoolarge', # Message length exceeds administrative limit '5.2.4' => 'systemerror', # Mailing list expansion problem '5.3.0' => 'systemerror', # Other or undefined mail system status '5.3.1' => 'systemfull', # Mail system full '5.3.2' => 'notaccept', # System not accepting network messages '5.3.3' => 'systemerror', # System not capable of selected features '5.3.4' => 'emailtoolarge', # Message too big for system '5.3.5' => 'systemerror', # System incorrectly configured '5.4.0' => 'networkerror', # Other or undefined network or routing status '5.4.3' => 'systemerror', # Directory server failure '5.4.4' => 'hostunknown', # Unable to route '5.5.2' => 'systemerror', # If the server cannot BASE64 decode any client response (AUTH) '5.5.3' => 'ratelimited', # Too many recipients '5.5.4' => 'systemerror', # Invalid command arguments '5.5.5' => 'systemerror', # Wrong protocol version '5.5.6' => 'syntaxerror', # Authentication Exchange line is too long '5.6.0' => 'contenterror', # Other or undefined media error '5.6.1' => 'contenterror', # Media not supported '5.6.2' => 'contenterror', # Conversion required and prohibited '5.6.3' => 'contenterror', # Conversion required but not supported '5.6.5' => 'contenterror', # Conversion Failed '5.6.6' => 'contenterror', # Message content not available '5.6.7' => 'rejected', # Non-ASCII addresses not permitted for that sender/recipient '5.6.8' => 'contenterror', # UTF-8 string reply is required, but not permitted by the SMTP client '5.6.9' => 'contenterror', # UTF-8 header message cannot be transferred to one or more recipients '5.7.0' => 'securityerror', # Other or undefined security status '5.7.1' => 'securityerror', # Delivery not authorized, message refused '5.7.2' => 'securityerror', # Mailing list expansion prohibited '5.7.3' => 'securityerror', # Security conversion required but not possible '5.7.4' => 'securityerror', # Security features not supported '5.7.5' => 'securityerror', # Cryptographic failure '5.7.6' => 'securityerror', # Cryptographic algorithm not supported '5.7.7' => 'securityerror', # Message integrity failure '5.7.8' => 'securityerror', # Authentication credentials invalid '5.7.9' => 'securityerror', # Authentication mechanism is too weak '5.7.10' => 'securityerror', # Encryption Needed '5.7.11' => 'securityerror', # Encryption required for requested authentication mechanism '5.7.13' => 'suspend', # User Account Disabled '5.7.14' => 'securityerror', # Trust relationship required '5.7.15' => 'securityerror', # Priority Level is too low '5.7.16' => 'emailtoolarge', # Message is too big for the specified priority '5.7.17' => 'hasmoved', # Mailbox owner has changed '5.7.18' => 'hasmoved', # Domain owner has changed '5.7.19' => 'systemerror', # RRVS test cannot be completed '5.7.20' => 'authfailure', # No passing DKIM signature found '5.7.21' => 'authfailure', # No acceptable DKIM signature found '5.7.22' => 'authfailure', # No valid author-matched DKIM signature found '5.7.23' => 'authfailure', # SPF validation failed '5.7.24' => 'authfailure', # SPF validation error '5.7.25' => 'requireptr', # Reverse DNS validation failed '5.7.26' => 'authfailure', # Multiple authentication checks failed '5.7.27' => 'notaccept', # MX resource record of a destination host is Null MX: RFC7505 '5.7.28' => 'spamdetected', # The message appears to be part of a mail flood of similar abusive messages. '5.7.29' => 'authfailure', # This status code may be returned when a message fails ARC validation. '5.7.30' => 'failedstarttls', # REQUIRETLS support required }.freeze
- InternalCode =
{ 'authfailure' => ['5.9.130', '4.9.130'], 'badreputation' => ['5.9.132', '4.9.132'], 'blocked' => ['5.9.134', '4.9.134'], 'contenterror' => ['5.9.160', '4.9.160'], 'emailtoolarge' => ['5.9.161', '4.9.161'], 'expired' => ['5.9.340', '4.9.340'], 'failedstarttls' => ['5.9.350', '4.9.350'], 'filtered' => ['5.9.210', '4.9.210'], 'hasmoved' => ['5.9.211', ''], 'hostunknown' => ['5.9.212', ''], 'mailboxfull' => ['5.9.220', '4.9.220'], 'mailererror' => ['5.9.230', '4.9.230'], 'networkerror' => ['5.9.341', '4.9.341'], 'norelaying' => ['5.9.214', '4.9.214'], 'notaccept' => ['5.9.215', '4.9.215'], 'notcompliantrfc' => ['5.9.162', '4.9.162'], 'onhold' => ['5.9.301', '4.9.301'], 'policyviolation' => ['5.9.371', '4.9.371'], 'ratelimited' => ['5.9.131', '4.9.131'], 'rejected' => ['5.9.110', '4.9.110'], 'requireptr' => ['5.9.133', '4.9.133'], 'securityerror' => ['5.9.370', '4.9.370'], 'spamdetected' => ['5.9.164', '4.9.164'], 'suppressed' => ['5.9.310', '4.9.310'], 'suspend' => ['5.9.221', '4.9.221'], 'syntaxerror' => ['5.9.351', '4.9.351'], 'systemerror' => ['5.9.231', '4.9.231'], 'systemfull' => ['5.9.232', '4.9.232'], 'undefined' => ['5.9.300', '4.9.300'], 'userunknown' => ['5.9.213', ''], 'virusdetected' => ['5.9.165', '4.9.165'], }.freeze
Class Method Summary collapse
-
.code(argv1 = "", argv2 = false) ⇒ String
Convert from the reason string to the internal status code.
-
.find(argv1 = nil, argv2 = '0') ⇒ String
Get a DSN code value from given string including DSN.
-
.is_ambiguous(argv1 = '') ⇒ Object
is_ambiguous() returns true when the argument is not empty and ends with “.0.0”.
-
.is_explicit(argv1 = '') ⇒ Object
is_explicit() returns false when the argument is empty or is an internal code.
-
.name(argv1 = nil) ⇒ String
Convert from the status code to the reason string.
-
.prefer(argv0 = nil, argv1 = nil, argv2 = nil) ⇒ String
Return the preferred value selected from the arguments.
-
.test(argv1 = '') ⇒ Boolean
Check whether a status code is a valid code or not.
Class Method Details
.code(argv1 = "", argv2 = false) ⇒ String
Convert from the reason string to the internal status code
649 650 651 652 653 654 |
# File 'lib/sisimai/smtp/status.rb', line 649 def code(argv1 = "", argv2 = false) return "" if argv1.empty? pairs = InternalCode[argv1]; return "" if pairs.nil? return argv2 ? pairs[1] : pairs[0] end |
.find(argv1 = nil, argv2 = '0') ⇒ String
Get a DSN code value from given string including DSN
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
# File 'lib/sisimai/smtp/status.rb', line 686 def find(argv1 = nil, argv2 = '0') return "" if argv1.to_s.empty? || argv1.size < 7 case argv2[0, 1] when '2', '4', '5' then eestatuses = [argv2[0, 1] + '.'] else eestatuses = ['5.', '4.', '2.'] end esmtperror = ' ' + argv1 + ' ' # Why 3 space characters? see https://github.com/sisimai/p5-sisimai/issues/574 lookingfor = [] Sisimai::RFC791.find(esmtperror).each do |e| # Rewrite an IPv4 address in the given string(argv1) with '***.***.***.***' p0 = esmtperror.index(e) || next esmtperror[p0, e.size] = '***.***.***.***' end eestatuses.each do |e| # Count the number of "5.", "4.", and "2." in the error message p0 = 0; p1 = 0 while p0 # Find all of the "5." and "4." string and store its postion p0 = esmtperror.index(e, p1) || break lookingfor << [p0, e] p1 = p0 + 5 end end return "" if lookingfor.size == 0 statuscode = [] # List of SMTP Status Code, Keep the order of appearances anotherone = '' # Alternative code readbuffer = '' characters = [] # Characters around the status code found by index() indexofees = nil # A position of SMTP status code found by the index() lookingfor.sort_by(&:first).each do |e| # Try to find an SMTP Status Code from the given string indexofees = esmtperror.index(e[1], e[0]); next if indexofees.nil? characters = [esmtperror[indexofees - 1, 1].ord] # [0] The previous character of the status [2, 3].each do |i| # [1] The value of the "Subject", "5.[7].261" # [2] "." chacater, a separator of the Subject and the Detail if indexofees + 1 + i > esmtperror.size characters << 0 else characters << esmtperror[indexofees + i, 1].ord end end next if characters[0] > 45 && characters[0] < 58 # Previous character is a number next if characters[0] == 86 || characters[0] == 118 # Avoid a version number("V" or "v") next if characters[1] < 48 || characters[1] > 55 # The value of the subject is not a number(0-7) next if characters[2] != 46 # It is not a "." character: a separator readbuffer = e[1] + characters[1].chr + '.' [4, 5, 6, 7].each do |i| # [3] The 1st digit of the detail # [4] The 2nd digit of the detail # [5] The 3rd digit of the detail # [6] The next character if indexofees + 1 + i > esmtperror.size characters << 0 else characters << esmtperror[indexofees + i, 1].ord end end next if characters[3] < 48 || characters[3] > 57 # The 1st digit of the detail is not a number readbuffer << characters[3].chr if Sisimai::SMTP::Status.is_ambiguous(readbuffer) || readbuffer == "4.4.7" # Find another status code except *.0.0, 4.4.7 anotherone = readbuffer next end if characters[4] < 48 || characters[4] > 57 # The 2nd digit of the detail is not a number statuscode << readbuffer next end readbuffer << characters[4].chr # The 2nd digit of the detail is a number if characters[5] < 48 || characters[5] > 57 # The 3rd digit of the detail is not a number statuscode << readbuffer next end readbuffer << characters[5].chr # The 3rd digit of the detail is a number next if characters[6] > 47 && characters[6] < 58 statuscode << readbuffer end statuscode << anotherone if anotherone.size > 0 return "" if statuscode.size == 0 # Select one from picked status codes cv = statuscode.shift; statuscode.each { |e| cv = Sisimai::SMTP::Status.prefer(cv, e, "") } return cv end |
.is_ambiguous(argv1 = '') ⇒ Object
is_ambiguous() returns true when the argument is not empty and ends with “.0.0”.
862 863 864 865 866 |
# File 'lib/sisimai/smtp/status.rb', line 862 def is_ambiguous(argv1 = '') return true if argv1.nil? || argv1.empty? return true if argv1.size == 5 && argv1.end_with?(".0.0") return false end |
.is_explicit(argv1 = '') ⇒ Object
is_explicit() returns false when the argument is empty or is an internal code
853 854 855 856 857 |
# File 'lib/sisimai/smtp/status.rb', line 853 def is_explicit(argv1 = '') return false if argv1.nil? || argv1.empty? return false if argv1.size == 7 && argv1.start_with?("5.9.", "4.9.") return true end |
.name(argv1 = nil) ⇒ String
Convert from the status code to the reason string
660 661 662 663 |
# File 'lib/sisimai/smtp/status.rb', line 660 def name(argv1 = nil) return "" if Sisimai::SMTP::Status.test(argv1.to_s) == false return StandardCode[argv1] || "" end |
.prefer(argv0 = nil, argv1 = nil, argv2 = nil) ⇒ String
Return the preferred value selected from the arguments
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 |
# File 'lib/sisimai/smtp/status.rb', line 793 def prefer(argv0 = nil, argv1 = nil, argv2 = nil) return argv1 if argv0.nil?; return argv1 if argv0.size < 5 return argv0 if argv1.nil?; return argv0 if argv1.size < 5 statuscode = argv0 codeinmesg = argv1; return codeinmesg if statuscode.index(".9.") == 1 esmtpreply = argv2 || '000' the1stchar = { 'field' => statuscode[0, 1].to_i, 'error' => codeinmesg[0, 1].to_i, 'reply' => esmtpreply.to_s[0, 1].to_i, } if the1stchar['reply'] > 0 && the1stchar['field'] != the1stchar['error'] # There is the 3rd argument (an SMTP Reply Code) # Returns the value of $argv0 or $argv1 which begins with the 1st character of argv2 return statuscode if the1stchar['reply'] == the1stchar['field'] return codeinmesg if the1stchar['reply'] == the1stchar['error'] end return statuscode if statuscode == codeinmesg zeroindex1 = {'field' => statuscode.index('.0') || -1, 'error' => codeinmesg.index('.0') || -1} zeroindex2 = {'field' => statuscode.index('.0.0') || -1, 'error' => codeinmesg.index('.0.0') || -1} if zeroindex2['field'] > 0 # "Status:" field is "X.0.0" return codeinmesg if zeroindex2['error'] < 0 return statuscode end if zeroindex1['field'] > 0 # "Status:" field is "X.Y.0" or "X.0.Z" return codeinmesg if zeroindex1['error'] < 0 end return statuscode if zeroindex2['error'] > 0 # An SMTP status code is "X.0.0" return codeinmesg if statuscode.start_with?('5.3.') # "5.3.Z" is an error of a system return codeinmesg if statuscode.end_with?('.5.1', '.5.2', '.5.4', '.5.5') case statuscode # - "4.4.7" is an ambigous code # - "4.7.0" indicates "too many errors" # - "X.5.1" indicates an invalid command # - "X.5.2" indicates a syntax error # - "X.5.4" indicates an invalid command argument # - "X.5.5" indicates a wrong protocol version when "4.4.7", "4.7.0" then return codeinmesg when "5.1.3" then return codeinmesg if codeinmesg.start_with?('5.7.') when "5.1.1" # "5.1.1" is a code of "userunknown" return statuscode if codeinmesg.start_with?('5.5.') || zeroindex1['error'] > 0 return codeinmesg end return statuscode end |
.test(argv1 = '') ⇒ Boolean
Check whether a status code is a valid code or not
670 671 672 673 674 675 676 677 678 679 680 |
# File 'lib/sisimai/smtp/status.rb', line 670 def test(argv1 = '') return false if argv1.to_s.empty? || argv1.size < 5 || argv1.size > 7 token = [] argv1.split('.').each { |e| token << e.to_i } return false if token.size != 3 return false if token[0] < 2 || token[0] == 3 || token[0] > 5 return false if token[1] < 0 || token[1] > 7 return false if token[2] < 0 return true end |