diff --git a/lib/sisimai/bite/email/activehunter.rb b/lib/sisimai/bite/email/activehunter.rb index cbbf45ca..94f3c29a 100644 --- a/lib/sisimai/bite/email/activehunter.rb +++ b/lib/sisimai/bite/email/activehunter.rb @@ -93,6 +93,7 @@ def scan(mhead, mbody) # ----- Transcript of session follows ----- # 550 sorry, no mailbox here by that name (#5.1.1 - chkusr) next unless e =~ /\A[0-9A-Za-z]+/ + next unless v['diagnosis'].empty? v['diagnosis'] = e end end diff --git a/lib/sisimai/bite/email/apachejames.rb b/lib/sisimai/bite/email/apachejames.rb index 88442dc3..ebbbfad1 100644 --- a/lib/sisimai/bite/email/apachejames.rb +++ b/lib/sisimai/bite/email/apachejames.rb @@ -11,7 +11,7 @@ class << self # apache-james-2.3.2/src/java/org/apache/james/transport/mailets/ # AbstractNotify.java|124: out.println("Error message below:"); # AbstractNotify.java|128: out.println("Message details:"); - message: ['Content-Disposition: inline'], + message: [''], rfc822: ['Content-Type: message/rfc822'], error: ['Error message below:'], }.freeze @@ -46,7 +46,7 @@ def scan(mhead, mbody) recipients = 0 # (Integer) The number of 'Final-Recipient' header diagnostic = '' # (String) Alternative diagnostic message subjecttxt = nil # (String) Alternative Subject text - gotmessage = -1 # (Integer) Flag for error message + gotmessage = nil # (Boolean) Flag for error message v = nil while e = hasdivided.shift do @@ -108,14 +108,14 @@ def scan(mhead, mbody) # Subject: Nyaaan subjecttxt = cv[1] else - next if gotmessage == 1 + next if gotmessage if v['diagnosis'] # Get an error message text if e.start_with?('Message details:') # Message details: # Subject: nyaan # ... - gotmessage = 1 + gotmessage = true else # Append error message text like the followng: # Error message below: @@ -126,6 +126,10 @@ def scan(mhead, mbody) # Error message below: # 550 - Requested action not taken: no such user here v['diagnosis'] = e if e == StartingOf[:error][0] + unless gotmessage + v['diagnosis'] ||= '' + v['diagnosis'] << ' ' + e + end end end end diff --git a/lib/sisimai/bite/email/gsuite.rb b/lib/sisimai/bite/email/gsuite.rb index d0237c4f..0c71e4d9 100644 --- a/lib/sisimai/bite/email/gsuite.rb +++ b/lib/sisimai/bite/email/gsuite.rb @@ -167,6 +167,7 @@ def scan(mhead, mbody) if anotherset['diagnosis'] # Continued error messages from the previous line like # "550 #5.1.0 Address rejected." + next if e =~ /\AContent-Type:/ next if emptylines > 5 if e.empty? # Count and next() diff --git a/lib/sisimai/bite/email/interscanmss.rb b/lib/sisimai/bite/email/interscanmss.rb index a9756901..5a760754 100644 --- a/lib/sisimai/bite/email/interscanmss.rb +++ b/lib/sisimai/bite/email/interscanmss.rb @@ -9,7 +9,7 @@ class << self Indicators = Sisimai::Bite::Email.INDICATORS StartingOf = { - message: ['Content-type: text/plain'], + message: [''], rfc822: ['Content-type: message/rfc822'], }.freeze diff --git a/lib/sisimai/data.rb b/lib/sisimai/data.rb index 1f8ee3ae..0d67a076 100644 --- a/lib/sisimai/data.rb +++ b/lib/sisimai/data.rb @@ -94,7 +94,6 @@ def self.make(data: nil, **argvs) return nil unless data.is_a? Sisimai::Message messageobj = data - mailheader = data.header rfc822data = messageobj.rfc822 fieldorder = { :recipient => [], :addresser => [] } objectlist = [] @@ -218,7 +217,7 @@ def self.make(data: nil, **argvs) next unless p['timestamp'] # OTHER_TEXT_HEADERS: - recvheader = mailheader['received'] || [] + recvheader = data.header['received'] || [] unless recvheader.empty? # Get localhost and remote host name from Received header. %w[lhost rhost].each { |a| e[a] ||= '' } diff --git a/lib/sisimai/message/email.rb b/lib/sisimai/message/email.rb index cdb308e3..7f5b2c41 100644 --- a/lib/sisimai/message/email.rb +++ b/lib/sisimai/message/email.rb @@ -29,7 +29,6 @@ class Email DefaultSet = Sisimai::Order::Email.another SubjectTab = Sisimai::Order::Email.by('subject') ExtHeaders = Sisimai::Order::Email.headers - ReEncoding = Sisimai::MIME.patterns # Make data structure from the email message(a body part and headers) # @param [Hash] argvs Email data @@ -416,19 +415,10 @@ def self.parse(argvs) end else # NOT text/plain - lowercased = bodystring.downcase - if lowercased =~ ReEncoding[:'quoted-print'] - # Content-Transfer-Encoding: quoted-printable - bodystring = Sisimai::MIME.qprintd(bodystring, mailheader) - end - - if lowercased =~ ReEncoding[:'7bit-encoded'] && - cv = lowercased.match(ReEncoding[:'some-iso2022']) - # Content-Transfer-Encoding: 7bit - # Content-Type: text/plain; charset=ISO-2022-JP - if ! cv[1].include?('us-ascii') && ! cv[1].include?('utf-8') - bodystring = Sisimai::String.to_utf8(bodystring, cv[1]) - end + if mesgformat.start_with?('multipart/') + # In case of Content-Type: multipart/* + p = Sisimai::MIME.makeflat(mailheader['content-type'], bodystring) + bodystring = p unless p.empty? end end diff --git a/lib/sisimai/mime.rb b/lib/sisimai/mime.rb index 69ace6ad..2f302d5f 100644 --- a/lib/sisimai/mime.rb +++ b/lib/sisimai/mime.rb @@ -7,11 +7,12 @@ class << self require 'sisimai/string' ReE = { - :'7bit-encoded' => %r/^content-transfer-encoding:[ ]*7bit$/m, - :'quoted-print' => %r/^content-transfer-encoding:[ ]*quoted-printable$/m, - :'some-iso2022' => %r/^content-type:[ ]*.+;[ ]*charset=["']?(iso-2022-[-a-z0-9]+?)['"]?$/m, - :'with-charset' => %r/^content[-]type:[ ]*.+[;][ ]*charset=['"]?(.+?)['"]?$/, - :'only-charset' => %r/^[\s\t]+charset=['"]?(.+?)['"]?$/, + :'7bit-encoded' => %r/^content-transfer-encoding:[ ]*7bit/m, + :'quoted-print' => %r/^content-transfer-encoding:[ ]*quoted-printable/m, + :'some-iso2022' => %r/^content-type:[ ]*.+;[ ]*charset=["']?(iso-2022-[-a-z0-9]+?)['"]?\b/m, + :'another-8bit' => %r/^content-type:[ ]*.+;[ ]*charset=["']?(.+?)['"]?\b/m, + :'with-charset' => %r/^content[-]type:[ ]*.+[;][ ]*charset=['"]?(.+?)['"]?\b/, + :'only-charset' => %r/^[\s\t]+charset=['"]?(.+?)['"]?\b/, :'html-message' => %r|^content-type:[ ]*text/html;|m, }.freeze @@ -53,10 +54,7 @@ def is_mimeencoded(argv1) def mimedecode(argvs = []) characterset = nil encodingname = nil - mimeencoded0 = nil decodedtext0 = [] - notmimetext0 = '' - notmimetext1 = '' while e = argvs.shift do # Check and decode each element @@ -66,13 +64,11 @@ def mimedecode(argvs = []) # MIME Encoded string if cv = e.match(/\A(.*)=[?]([-_0-9A-Za-z]+)[?]([BbQq])[?](.+)[?]=?(.*)\z/) # =?utf-8?B?55m954yr44Gr44KD44KT44GT?= - notmimetext0 = cv[1] characterset ||= cv[2] encodingname ||= cv[3] mimeencoded0 = cv[4] - notmimetext1 = cv[5] - decodedtext0 << notmimetext0 + decodedtext0 << cv[1] if encodingname == 'Q' # Quoted-Printable decodedtext0 << mimeencoded0.unpack('M').first @@ -81,7 +77,7 @@ def mimedecode(argvs = []) # Base64 decodedtext0 << Base64.decode64(mimeencoded0) end - decodedtext0 << notmimetext1 + decodedtext0 << cv[5] end else decodedtext0 << e @@ -129,13 +125,10 @@ def qprintd(argv1 = nil, heads = {}) boundary01 = Sisimai::MIME.boundary(heads['content-type'], 1) bodystring = '' notdecoded = '' - getencoded = '' - lowercased = '' encodename = nil ctencoding = nil mimeinside = false - mustencode = false hasdivided = argv1.split("\n") while e = hasdivided.shift do @@ -149,8 +142,8 @@ def qprintd(argv1 = nil, heads = {}) if e == boundary00 # The next boundary string has appeared # --=_gy7C4Gpes0RP4V5Bs9cK4o2Us2ZT57b-3OLnRN+4klS8dTmQ - getencoded = Sisimai::String.to_utf8(notdecoded.unpack('M').first, encodename) - bodystring << getencoded << e + "\n" + hasdecoded = Sisimai::String.to_utf8(notdecoded.unpack('M').first, encodename) + bodystring << hasdecoded << e + "\n" notdecoded = '' mimeinside = false @@ -256,15 +249,206 @@ def boundary(argv1 = nil, start = -1) # Content-Type: multipart/report; report-type=delivery-status; # boundary="n6H9lKZh014511.1247824040/mx.example.jp" value = cv[1] - value.delete!(%q|'"|) + value.delete!(%q|'";\\|) value = '--' + value if start > -1 value = value + '--' if start > 0 end return value end - end + # Breaks up each multipart/* block + # @param [String] argv0 Text block of multipart/* + # @param [String] argv1 MIME type of the outside part + # @return [String] Decoded part as a plain text(text part only) + def breaksup(argv0 = nil, argv1 = '') + return nil unless argv0 + + hasflatten = '' # Message body including only text/plain and message/* + alsoappend = %r{\A(?:text/rfc822-headers|message/)} + thisformat = %r/\A(?:Content-Transfer-Encoding:\s*.+\n)?Content-Type:\s*([^ ;]+)/ + leavesonly = %r{\A(?> + text/(?:plain|html|rfc822-headers) + |message/(?:x?delivery-status|rfc822|partial|feedback-report) + |multipart/(?:report|alternative|mixed|related|partial) + ) + }x + + mimeformat = '' # MIME type string of this part + alternates = argv1.start_with?('multipart/alternative') ? true : false + + if cv = argv0.match(thisformat) + # Get MIME type string from Content-Type: "..." field at the first line + # or the second line of the part. + mimeformat = cv[1].downcase + end + + # Sisimai require only MIME types defined in $leavesonly variable + return '' unless mimeformat =~ leavesonly + return '' if alternates && mimeformat == 'text/html' + + (upperchunk, lowerchunk) = argv0.split(/^$/m, 2) + upperchunk.gsub!("\n", ' ').squeeze(' ') + + # Content-Description: Undelivered Message + # Content-Type: message/rfc822 + # + lowerchunk ||= '' + + if mimeformat.start_with?('multipart/') + # Content-Type: multipart/* + mpboundary = Regexp.new(Regexp.escape(Sisimai::MIME.boundary(upperchunk, 0)) << "\n") + innerparts = lowerchunk.split(mpboundary) + + innerparts.shift if innerparts[0].empty? + while e = innerparts.shift do + # Find internal multipart/* blocks and decode + if cv = e.match(thisformat) + # Found "Content-Type" field at the first or second line of this + # splitted part + nextformat = cv[1].downcase + + next unless nextformat =~ leavesonly + next if nextformat == 'text/html' + + hasflatten << Sisimai::MIME.breaksup(e, mimeformat) + else + # The content of this part is almost '--': a part of boundary + # string which is used for splitting multipart/* blocks. + hasflatten << "\n" + end + end + else + # Is not "Content-Type: multipart/*" + if cv = upperchunk.match(/Content-Transfer-Encoding: ([^\s;]+)/) + # Content-Transfer-Encoding: quoted-printable|base64|7bit|... + ctencoding = cv[1].downcase + getdecoded = '' + + if ctencoding == 'quoted-printable' + # Content-Transfer-Encoding: quoted-printable + getdecoded = Sisimai::MIME.qprintd(lowerchunk) + + elsif ctencoding == 'base64' + # Content-Transfer-Encoding: base64 + getdecoded = Sisimai::MIME.base64d(lowerchunk) + + elsif ctencoding == '7bit' + # Content-Transfer-Encoding: 7bit + if cv = upperchunk.downcase.match(ReE[:'some-iso2022']) + # Content-Type: text/plain; charset=ISO-2022-JP + getdecoded = Sisimai::String.to_utf8(lowerchunk, cv[1]) + else + # No "charset" parameter in Content-Type field + getdecoded = lowerchunk + end + else + # Content-Transfer-Encoding: 8bit, binary, and so on + getdecoded = lowerchunk + end + + # - Convert CRLF to LF + # - Delete HTML tags inside of text/html part whenever possible + getdecoded.gsub!(/\r\n/, "\n") + getdecoded.gsub!(/[<][^@ ]+?[>]/, '') if mimeformat == 'text/html' + + if mimeformat =~ alsoappend + # Append field when the value of Content-Type: begins with + # message/ or equals text/rfc822-headers. + upperchunk.sub!(/Content-Transfer-Encoding:.+\z/, '').gsub!(/[ ]\z/, '') + hasflatten << upperchunk + end + + unless getdecoded.empty? + # The string will be encoded to UTF-8 forcely and call String#scrub + # method to avoid the following errors: + # - incompatible character encodings: ASCII-8BIT and UTF-8 + # - invalid byte sequence in UTF-8 + unless getdecoded.encoding.to_s == 'UTF-8' + if cv = upperchunk.downcase.match(ReE[:'another-8bit']) + # ISO-8859-1, GB2312, and so on + getdecoded = Sisimai::String.to_utf8(getdecoded, cv[1]) + end + end + # A part which has no "charset" parameter causes an ArgumentError: + # invalid byte sequence in UTF-8 so String#scrub should be called + hasflatten << getdecoded.scrub!('?') << "\n\n" + end + else + # Content-Type: text/plain OR text/rfc822-headers OR message/* + if mimeformat.start_with?('message/') || mimeformat == 'text/rfc822-headers' + # Append headers of multipart/* when the value of "Content-Type" + # is inlucded in the following MIME types: + # - message/delivery-status + # - message/rfc822 + # - text/rfc822-headers + hasflatten << upperchunk + end + lowerchunk.sub!(/^--\z/m, '') + lowerchunk << "\n" unless lowerchunk =~ /\n\z/ + hasflatten << lowerchunk + end + end + + return hasflatten + end + + # MIME decode entire message body + # @param [String] argv0 Content-Type header + # @param [String] argv1 Entire message body + # @return [String] Decoded message body + def makeflat(argv0 = nil, argv1 = nil) + return nil unless argv0 + return nil unless argv1 + + ehboundary = Sisimai::MIME.boundary(argv0, 0) + mimeformat = '' + multiparts = [] + bodystring = '' + + if cv = argv0.match(%r|\A([0-9a-z]+/[^ ;]+)|) + # Get MIME type string from an email header given as the 1st argument + mimeformat = cv[1] + end + return '' unless mimeformat.include?('multipart/') + return '' if ehboundary.empty? + + # Some bounce messages include lower-cased "content-type:" field such as + # content-type: message/delivery-status + # content-transfer-encoding: quoted-printable + argv1.gsub!(/[Cc]ontent-[Tt]ype:/m, 'Content-Type:') + argv1.gsub!(/[Cc]ontent-[Tt]ransfer-[Ee]ncodeing:/m, 'Content-Transfer-Encoding:') + + # 1. Some bounce messages include upper-cased "Content-Transfer-Encoding", + # and "Content-Type" value such as + # - Content-Type: multipart/RELATED; + # - Content-Transfer-Encoding: 7BIT + # 2. Unused fields inside of mutipart/* block should be removed + argv1.gsub!(/(Content-[A-Za-z-]+?):[ ]*([^\s]+)/) do "#{$1}: #{$2.downcase}" end + argv1.gsub!(/^Content-(?:Description|Disposition):.+$/, '') + + multiparts = argv1.split(Regexp.new(Regexp.escape(ehboundary) << "\n")) + multiparts.shift if multiparts[0].empty? + + while e = multiparts.shift do + # Find internal multipart blocks and decode + if e =~ /\A(?:Content-[A-Za-z-]+:.+?\r\n)?Content-Type:[ ]*[^\s]+/ + # Content-Type: multipart/* + bodystring << Sisimai::MIME.breaksup(e, mimeformat) + else + # Is not multipart/* block + e.sub!(%r|^Content-Transfer-Encoding:.+?\n|mi, '') + e.sub!(%r|^Content-Type:\s*text/plain.+?\n|mi, '') + bodystring << e + end + end + bodystring.gsub!(%r{^(Content-Type:\s*message/(?:rfc822|delivery-status)).+$}, '\1') + bodystring.gsub!(/^\n{2,}/, "\n") + + return bodystring + end + + end end end diff --git a/lib/sisimai/rfc2606.rb b/lib/sisimai/rfc2606.rb deleted file mode 100644 index df52c8ad..00000000 --- a/lib/sisimai/rfc2606.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Sisimai - # Sisimai::RFC2606 checks that the domain part of the email address in the - # argument is reserved or not. - module RFC2606 - # Imported from p5-Sisimail/lib/Sisimai/RFC2606.pm - class << self - # Whether domain part is Reserved or not - # @param [String] dpart Domain part - # @return [True,False] true: is Reserved top level domain - # false: is NOT reserved top level domain - # @see http://www.ietf.org/rfc/rfc2606.txt - def is_reserved(argv = '') - return false unless argv - return false unless argv.is_a?(::String) - - return true if argv =~ /[.](?:test|example|invalid|localhost)\z/ - return true if argv =~ /example[.](?:com|net|org|jp)\z/ - return true if argv =~ /example[.](?:ac|ad|co|ed|go|gr|lg|ne|or)[.]jp\z/ - return false - end - end - end -end diff --git a/lib/sisimai/rfc3464.rb b/lib/sisimai/rfc3464.rb index fbb51fcf..0ff80698 100644 --- a/lib/sisimai/rfc3464.rb +++ b/lib/sisimai/rfc3464.rb @@ -10,7 +10,7 @@ class << self MarkingsOf = { message: %r{\A(?> content-type:[ ]*(?: - message/delivery-status + message/x?delivery-status |message/disposition-notification |text/plain;[ ]charset= ) @@ -128,13 +128,10 @@ def scan(mhead, mbody) recipients += 1 elsif cv = e.match(/\AX-Actual-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/) - # X-Actual-Recipient: - if cv[1] =~ /[ \t]+/ - # X-Actual-Recipient: RFC822; |IFS=' ' && exec procmail -f- || exit 75 ... - else - # X-Actual-Recipient: rfc822; kijitora@neko.example.jp - v['alias'] = cv[1] - end + # X-Actual-Recipient: RFC822; |IFS=' ' && exec procmail -f- || exit 75 ... + # X-Actual-Recipient: rfc822; kijitora@neko.example.jp + v['alias'] = cv[1] unless cv[1] =~ /[ \t]+/ + elsif cv = e.match(/\AAction:[ ]*(.+)\z/) # 2.3.3 Action field # The Action field indicates the action performed by the Reporting-MTA diff --git a/lib/sisimai/rfc5322.rb b/lib/sisimai/rfc5322.rb index 53585b10..8dc1c6b7 100644 --- a/lib/sisimai/rfc5322.rb +++ b/lib/sisimai/rfc5322.rb @@ -8,9 +8,7 @@ class << self :subject => ['Subject'], :listid => ['List-Id'], :date => %w[Date Posted-Date Posted Resent-Date], - :addresser => %w[ - From Return-Path Reply-To Errors-To Reverse-Path X-Postfix-Sender Envelope-From X-Envelope-From - ], + :addresser => %w[From Return-Path Reply-To Errors-To Reverse-Path X-Postfix-Sender Envelope-From X-Envelope-From], :recipient => %w[To Delivered-To Forward-Path Envelope-To X-Envelope-To Resent-To Apparently-To], }.freeze diff --git a/set-of-emails/maildir/bsd/email-apachejames-01.eml b/set-of-emails/maildir/bsd/email-apachejames-01.eml index 25159981..09b32c1b 100644 --- a/set-of-emails/maildir/bsd/email-apachejames-01.eml +++ b/set-of-emails/maildir/bsd/email-apachejames-01.eml @@ -13,8 +13,7 @@ Message-ID: <00000000.000000.0000000000000.JavaMail.c@example.com> In-Reply-To: <00000000000000.00000.qmail@example.com> Subject: Re:Test message MIME-Version: 1.0 -Content-Type: multipart/mixed; -boundary="----=_Part_000000_0000000.000000000000" +Content-Type: multipart/mixed; boundary="----=_Part_000000_0000000.000000000000" Date: 12 Jun 2013 02:21:53 -0000 ------=_Part_000000_0000000.000000000000 diff --git a/set-of-emails/maildir/bsd/rfc3464-02.eml b/set-of-emails/maildir/bsd/email-domino-03.eml similarity index 100% rename from set-of-emails/maildir/bsd/rfc3464-02.eml rename to set-of-emails/maildir/bsd/email-domino-03.eml diff --git a/set-of-emails/maildir/bsd/email-office365-08.eml b/set-of-emails/maildir/bsd/email-office365-08.eml index 5b8706bc..6c043511 100644 --- a/set-of-emails/maildir/bsd/email-office365-08.eml +++ b/set-of-emails/maildir/bsd/email-office365-08.eml @@ -1,30 +1,30 @@ Received: from CY1NAM02HT231.eop-nam02.prod.protection.outlook.com -(2603:1096:100:18::27) by SLXP216MB0381.KORP216.PROD.OUTLOOK.COM with HTTPS -via SL2P216CA0017.KORP216.PROD.OUTLOOK.COM; Tue, 19 Jun 2018 07:32:09 +0000 + (2603:1096:100:18::27) by SLXP216MB0381.KORP216.PROD.OUTLOOK.COM with HTTPS + via SL2P216CA0017.KORP216.PROD.OUTLOOK.COM; Tue, 19 Jun 2018 07:32:09 +0000 Received: from CY1NAM02FT026.eop-nam02.prod.protection.outlook.com -(10.152.74.57) by CY1NAM02HT231.eop-nam02.prod.protection.outlook.com -(10.152.74.213) with Microsoft SMTP Server (version=TLS1_2, -cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.863.11; Tue, 19 -Jun 2018 07:32:07 +0000 + (10.152.74.57) by CY1NAM02HT231.eop-nam02.prod.protection.outlook.com + (10.152.74.213) with Microsoft SMTP Server (version=TLS1_2, + cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.863.11; Tue, 19 + Jun 2018 07:32:07 +0000 Authentication-Results: spf=pass (sender IP is 104.47.126.209) -smtp.helo=APC01-PU1-obe.outbound.protection.outlook.com; outlook.com; -dkim=none (message not signed) header.d=none;outlook.com; dmarc=pass -action=none header.from=outlook.com; + smtp.helo=APC01-PU1-obe.outbound.protection.outlook.com; outlook.com; + dkim=none (message not signed) header.d=none;outlook.com; dmarc=pass + action=none header.from=outlook.com; Received-SPF: Pass (protection.outlook.com: domain of -APC01-PU1-obe.outbound.protection.outlook.com designates 104.47.126.209 as -permitted sender) receiver=protection.outlook.com; client-ip=104.47.126.209; -helo=APC01-PU1-obe.outbound.protection.outlook.com; + APC01-PU1-obe.outbound.protection.outlook.com designates 104.47.126.209 as + permitted sender) receiver=protection.outlook.com; client-ip=104.47.126.209; + helo=APC01-PU1-obe.outbound.protection.outlook.com; Received: from APC01-PU1-obe.outbound.protection.outlook.com (104.47.126.209) -by CY1NAM02FT026.mail.protection.outlook.com (10.152.75.157) with Microsoft -SMTP Server (version=TLS1_2, -cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.20.863.11 via -Frontend Transport; Tue, 19 Jun 2018 07:32:07 +0000 + by CY1NAM02FT026.mail.protection.outlook.com (10.152.75.157) with Microsoft + SMTP Server (version=TLS1_2, + cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.20.863.11 via + Frontend Transport; Tue, 19 Jun 2018 07:32:07 +0000 X-IncomingTopHeaderMarker: OriginalChecksum:6EFCA1BC79F2ADDA6F71136C1760587E715ACEEC87591EAF1512DA513842DE47;UpperCasedChecksum:83D5CA817A0517B628E1248C4B635477755FBA754058C143B59025142522C933;SizeAsReceived:6211;Count:31 From: Microsoft Outlook To: Date: Tue, 19 Jun 2018 07:32:05 +0000 Content-Type: multipart/report; report-type=delivery-status; -boundary="ba016ad5-e34f-4e12-b45d-abfd610b3b50" + boundary="ba016ad5-e34f-4e12-b45d-abfd610b3b50" X-MS-Exchange-Message-Is-Ndr: Content-Language: en-US Message-ID: <237fe034-588c-4325-b6f9-c5ad038c1c55@SG2APC01HT007.mail.protection.outlook.com> @@ -41,39 +41,38 @@ X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(444000031);SRVR:SG2APC0 X-Forefront-PRVS: 07083FF734 X-Forefront-Antispam-Report-Untrusted: SFV:NSPM;SFS:(10019020)(4640300001)(4630300001)(366004)(199004)(189003)(45914002)(1930700014)(11346002)(476003)(2351001)(260700001)(31686004)(575784001)(446003)(82202002)(84326002)(97736004)(45080400002)(10290500003)(486006)(966005)(42882007)(31696002)(105586002)(106356001)(7350300001)(20460500001)(102106001)(78352004)(102836004)(1476002)(68736007)(81156014)(59450400001)(52396003)(8676002)(89962001)(1706002)(53546011)(76176011)(6916009)(42186006)(6636002)(5890100001)(53906005)(6306002)(16586007)(22452003)(87572001)(6346003)(83832001)(236005)(5660300001)(11286001);DIR:OUT;SFP:1501;SCL:1;SRVR:SG2APC01HT007;H:;FPR:;SPF:None;PTR:InfoNoRecords;A:0;MX:0;LANG:; Received-SPF: None (protection.outlook.com: does not designate permitted -sender hosts) + sender hosts) Authentication-Results-Original: spf=none (sender IP is ) smtp.mailfrom=<>; X-Microsoft-Exchange-Diagnostics-untrusted: =?us-ascii?Q?1;SG2APC01HT007;23:n8yOj0Rmp3da/DEadJP2heE5Egfp2JXkT2iLuLDUT?= -=?us-ascii?Q?caYHaw7pC4BNFhKEZxXwj8Ofer3OtYKrsJikDMm2Yx0uatB5k/4GDl4sM3JH?= -=?us-ascii?Q?DBYWVX2MHcuRZfh1nmTPza6RIAFV8Vk1vuOJIYqFOplFF4SNANkqb177DagO?= -=?us-ascii?Q?KLBI5/9BOA3ifcPjoA4LPQK5L3n8cL6JTLlYrT4sLxZJnaKk0YwY1k3bNGxr?= -=?us-ascii?Q?VKplziW0npnMSlpNw2RqZJvAGfsNxQvwQKhWmnb+kYSmSAeQnEGwhpDHs7q4?= -=?us-ascii?Q?E4ErdFJ0D04iwmkK/G4+hcQyGKCbyWM0B1b+wN0NoSUN3Gdkb1S2tE/Nx6My?= -=?us-ascii?Q?1mmL8tAKouopECHT++TtL0RySwWVe9BvVwaJ0VpD5YjteLghxinr2Umam3nk?= -=?us-ascii?Q?k0mO1ys+MO6WKN0FYCsY55s0u+GmN2u//CKxq+Po3MHrZ147Pl8qw2OuiOT4?= -=?us-ascii?Q?o6W37KBWmaf8QydqJhBrDaFJqXVemfcrQZqd0mkA+MqV8bp9f/4uDyYGwKTz?= -=?us-ascii?Q?R6jMhA5tOEdnSXt8wotie8uhkeUUGG6+MD0Z65aiRNvQDilXWRRusz109TUg?= -=?us-ascii?Q?C8YrvZVfSCcc1wM+HJ6q7EDH8v2XvF9n67fJwf1I+vmo3ZVFw28DONSRLf5E?= -=?us-ascii?Q?Eu6dPJP7LIZi7/I903m+UfHO+wVHLpf4iSMYHcxJyKhXZgRQDXtlmHeMuIZA?= -=?us-ascii?Q?/cuZbyUXMwEkXbeelFyAJAhZ5n48qdOYXErQgqvAS2PAVZNBfnm7QlsTDQPv?= -=?us-ascii?Q?sNzYuj0GyBJTZEvc4mH34JbUaIWHwwIuDkdA7vgtO7vDHIkWS9TbwZjJlVBH?= -=?us-ascii?Q?BteXdX6x+SEZClKrUPBSK6zaW0G5SR8arM7HBpJz8rWsAZpz3LvfkB0UzloZ?= -=?us-ascii?Q?g0rueC/maezZRwZAPL4gOq4O9CXFO2mhyUYC7NE7TOSGp//KoGdHF1g5gVle?= -=?us-ascii?Q?6HP2t3/JuMQ9PCk8TnoFoFoU5OaXnAjefGNzRFcO24IU83MxbnczdsANlov6?= -=?us-ascii?Q?tkLWtKsZSIj3HiJqu39kD9vIni7syNw5qHKTq+uDzXBf1lLnlVZ/k1WB9h+n?= -=?us-ascii?Q?YnUuyYxAOC5ziBXjsJPM+2ObC4ZNRoisraFrh28IMhS8xrcqZmlwUwcQUojq?= -=?us-ascii?Q?JTkyMMCR47x0N03wmc5OndvQvJYFWcAJ4nIBBJCP9qOxuaUuiHWGj+JPE+vQ?= -=?us-ascii?Q?pG2dotv3krGlpEvTSbza4LA97UkCFkYMlV0vz9bj3aJJXcW5SsI+wSWzmzeT?= -=?us-ascii?Q?jQuElqWN0wrjgbcLFhQydJZo6cptQRWOD7K+MGMIwTj9qzCoyq4U0WJyAw49?= -=?us-ascii?B?Zz09?= + =?us-ascii?Q?caYHaw7pC4BNFhKEZxXwj8Ofer3OtYKrsJikDMm2Yx0uatB5k/4GDl4sM3JH?= + =?us-ascii?Q?DBYWVX2MHcuRZfh1nmTPza6RIAFV8Vk1vuOJIYqFOplFF4SNANkqb177DagO?= + =?us-ascii?Q?KLBI5/9BOA3ifcPjoA4LPQK5L3n8cL6JTLlYrT4sLxZJnaKk0YwY1k3bNGxr?= + =?us-ascii?Q?VKplziW0npnMSlpNw2RqZJvAGfsNxQvwQKhWmnb+kYSmSAeQnEGwhpDHs7q4?= + =?us-ascii?Q?E4ErdFJ0D04iwmkK/G4+hcQyGKCbyWM0B1b+wN0NoSUN3Gdkb1S2tE/Nx6My?= + =?us-ascii?Q?1mmL8tAKouopECHT++TtL0RySwWVe9BvVwaJ0VpD5YjteLghxinr2Umam3nk?= + =?us-ascii?Q?k0mO1ys+MO6WKN0FYCsY55s0u+GmN2u//CKxq+Po3MHrZ147Pl8qw2OuiOT4?= + =?us-ascii?Q?o6W37KBWmaf8QydqJhBrDaFJqXVemfcrQZqd0mkA+MqV8bp9f/4uDyYGwKTz?= + =?us-ascii?Q?R6jMhA5tOEdnSXt8wotie8uhkeUUGG6+MD0Z65aiRNvQDilXWRRusz109TUg?= + =?us-ascii?Q?C8YrvZVfSCcc1wM+HJ6q7EDH8v2XvF9n67fJwf1I+vmo3ZVFw28DONSRLf5E?= + =?us-ascii?Q?Eu6dPJP7LIZi7/I903m+UfHO+wVHLpf4iSMYHcxJyKhXZgRQDXtlmHeMuIZA?= + =?us-ascii?Q?/cuZbyUXMwEkXbeelFyAJAhZ5n48qdOYXErQgqvAS2PAVZNBfnm7QlsTDQPv?= + =?us-ascii?Q?sNzYuj0GyBJTZEvc4mH34JbUaIWHwwIuDkdA7vgtO7vDHIkWS9TbwZjJlVBH?= + =?us-ascii?Q?BteXdX6x+SEZClKrUPBSK6zaW0G5SR8arM7HBpJz8rWsAZpz3LvfkB0UzloZ?= + =?us-ascii?Q?g0rueC/maezZRwZAPL4gOq4O9CXFO2mhyUYC7NE7TOSGp//KoGdHF1g5gVle?= + =?us-ascii?Q?6HP2t3/JuMQ9PCk8TnoFoFoU5OaXnAjefGNzRFcO24IU83MxbnczdsANlov6?= + =?us-ascii?Q?tkLWtKsZSIj3HiJqu39kD9vIni7syNw5qHKTq+uDzXBf1lLnlVZ/k1WB9h+n?= + =?us-ascii?Q?YnUuyYxAOC5ziBXjsJPM+2ObC4ZNRoisraFrh28IMhS8xrcqZmlwUwcQUojq?= + =?us-ascii?Q?JTkyMMCR47x0N03wmc5OndvQvJYFWcAJ4nIBBJCP9qOxuaUuiHWGj+JPE+vQ?= + =?us-ascii?Q?pG2dotv3krGlpEvTSbza4LA97UkCFkYMlV0vz9bj3aJJXcW5SsI+wSWzmzeT?= + =?us-ascii?Q?jQuElqWN0wrjgbcLFhQydJZo6cptQRWOD7K+MGMIwTj9qzCoyq4U0WJyAw49?= + =?us-ascii?B?Zz09?= X-Microsoft-Antispam-Message-Info-Original: bA94fjaHy+xAgBLnQdwb3ehd45WaWHmLpITi6cS9HpIqy4npiCQbUmmIwJ/1o6bUG33sk/akeSkuvkmzF0K//E3qewyaNWP9I0FVaWcL5nVaPFzQpaFh+d8EwycgYwsIT1e/9kQTM2nII5lwQ/P1fV4jOISlIUQX3p7wJi06k06LXhiyj+3PKZeGLcPkCXjra38ITR04t8k6cQ+7gRK57A== X-Microsoft-Exchange-Diagnostics-untrusted: 1;SG2APC01HT007;6:9EDfORUNRTWrCt8oXfrTDvJwVTiuocVUa8Wgz89CNrJxDKGBCvPc1NyZQKywab7+IhtpxxRPfZeKUjv/xf95VM0sLPE9VD75O04ZNx8Q04aSjdRrq45sNWyB+7ABJlocNmvPRci2+8phghepIi+7WMmjILPw9GXTy2eWyMCuDTok6oIk2XQRZFv0hwwzVYXUjCkH0Og0UG1ZWEeJNS9rjWex7TkZhENDWS733iJoZWf2tMA+eCyFeMNC9LVa6fKpAEt2ZLwBHCQkrTNWfpi8CPjxFglBi7GngyY2SRbEy/CMrt397qQFf9Ltr69Y3AtvJVduk6EoYhlxSOfHHhLgu1hZuVVPBvoZB3pbRS9drnv3U1OVj+3fRqzJUIrl5LJ78iIRwv0K9ej8GRBNPyrqtp3P+OG3qEBd4MffPkDoCrEEytWE6gl3MyemzHzxiWjuCdBYa9fRtZyjr7a6gAoMew==;5:dRv8YL5Jv8Fp5+Rj+vGFLmpWCxwzla/U3W20j+Uv10fgETwa04lUTGWYXl2B030tdYKrRfHkoXTAakzkziiDRM+Zpokug4LmcrYP5sr9E7Q3xXLi+gagpmjJ5Fb5R9a8HINkfQrbUILw6WiZQ+XIYKmoJlSfz+rvLl8S7sGxFIE=;24:2fsmtD4etECRA3jsAdPRdsT2qAy3hbNEmkIEYgXQZXkkU1/RQ679x9D5gqaI57PHpA81D5Xtj5ObsU4/DAAfsG32R+OmImzpTo69i29qCgc= X-Microsoft-Exchange-Diagnostics-untrusted: 1;SG2APC01HT007;7:Nq03PBvyGEaOWolYg/CKTWlSBzztzNSsYbww81cZtQzr4FonJ2lAadMfIUUCktv0GfaIpZkpVZ8zCWuUWDVmIWhH4kjjd1FbedPHT2+MHV8Ifwf7W32EonOWCRCQm3zwwBoTtngw+8x9vHsd8AHOTr3XBlqZShapuCWtRrryFulOm5KV2Z0HIQn2HBroMACDDiaz2NrNIrFGDwfzjrd2iao8jAVaPoPNMAxdvyFeoaSk+KGoYL3aOzhvP41a0dQh X-MS-Exchange-Transport-CrossTenantHeadersStamped: SG2APC01HT007 X-IncomingHeaderCount: 31 Return-Path: <> -X-MS-Exchange-Organization-ExpirationStartTime: 19 Jun 2018 07:32:07.6821 -(UTC) +X-MS-Exchange-Organization-ExpirationStartTime: 19 Jun 2018 07:32:07.6821 (UTC) X-MS-Exchange-Organization-ExpirationStartTimeReason: Original Submit X-MS-Exchange-Organization-ExpirationInterval: 2:00:00:00.0000000 X-MS-Exchange-Organization-ExpirationIntervalReason: Original Submit @@ -100,8 +99,7 @@ X-Microsoft-Exchange-Diagnostics: 1;CY1NAM02HT231;4:g3mKNwQGNfdcidpPnINexItInz2/ X-Microsoft-Antispam-Message-Info: MgfXVxOQhB38r0/m5rBy1yzY48pthf3Pz5EemSJNRKQQHP/tSah2LNe2cltTu9HG/jRZj2hhfvuM+tNABh/C2MWDJMKKa1D1ZkabQ2Oi4FBWnTvh+vvVw63oiyYDAfsV2aBkH3KECxKDgfmtU7I72DnTnumtkTpTIg38ZnNoU7g45vQF4lGw7ljMk8zWyPxDHx9aAiPhjhBzpto05zz5tFjgQgaCikEVbx3M6WDmWNDnlJOK7zDPG8V28ufZcVufnlCN+Db9Bt3HoXEwCswgpkDpZV9PdRQ4xp/WeD1CX7Ss7EieSjWrHsXsWK4wRkVQLwTvPROPvUOGWnVEuV3Ol+3Fu6vGeiUXJv+iYNqRTd0= X-Microsoft-Exchange-Diagnostics: 1;CY1NAM02HT231;6:+HMydG1jaFx7FHK/PTW9ze4vRI5dYuQZQyv3a3Kcc3EbWJtyvHKcGCMYusSYLLv37ZfsS5pti3QtmbWn+7SjY6Xggc5GV81whlA90irlc//OuRBPl/9EXFsC7Ucso5VNAzznbnV7cGXnxiF9qAGYR9R01PYS6ZhO3absIdzSx4/0aO1cy7FMadEaJ+EPUh31BPPt3YDKgfQjo6hE1jNGGOeO4O+jHENwmUbUkR5IYZwcYTm7rLpLucxG9DwLCauNQ9ZE6jPSFz2YXyaHYeokhlhldYPJkculsJQ+T0yETaozis2ScKovo2vlSQUBV3r5Bv1/zmoe/lxutsMwJga/1fHjJMF/Gqc3463XewgDqLiPzKTc9S56vphB9dV0MNV51XJ4lZVH94fKqR8iskjHkkH8oAJpwggmFjIc42CSvcsApNilqmwBfJWc/A3FzTmKQvRSHXYG7RX1eCwQdRE4fQ==;5:y33iWIcKSCGQjHvkm5vNxOPISVGMFDiTuXq135jhTfjctb7E5A2YS81tnIOCMZDf8Y4KwbXxIc7UT8zcXIABkUUhdVQRzhgZI1zZe/teF92WovCSVjg7rYnxEmKjNe2mhSli1sq6DOxDIKYD40kClorP5Cw0KNcg6Q/2BcDiX/A=;7:5RU/fJaTDq9m6orldwd1N7AtHdOnMTik/RyhYlbQYct1M92V/80DG3TP+8sTWqyK21HSfAK3rFo4O1zp236Tzek2N8G6By+CMDmqQdrNrTeAUHNZbM9D/rDY2Jn/5fOU951aHrlIdBBKFVMpjCQFAGU1mD5Ie2T7TFKHd2g+oPqI8zLOlGo39nZJn8Dc6iDoT1ep6O7JEFUAHj7qMOj3WZzzHE/uhzQNB0Ac1m8i6oebmRAANPxPVjQa8haBCyJ6 X-OriginatorOrg: outlook.com -X-MS-Exchange-CrossTenant-OriginalArrivalTime: 19 Jun 2018 07:32:07.2446 -(UTC) +X-MS-Exchange-CrossTenant-OriginalArrivalTime: 19 Jun 2018 07:32:07.2446 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 59f77485-b88d-4476-0bde-08d5d5b6c31a X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-FromEntityHeader: Internet @@ -113,24 +111,24 @@ X-Microsoft-Exchange-Diagnostics: X-Microsoft-Antispam-Mailbox-Delivery: abwl:0;wl:0;pcwl:0;kl:0;iwl:0;dwl:0;dkl:0;rwl:0;ex:0;auth:1;dest:I;OFR:SpamFilterPass;ENG:(5062000261)(5061607266)(5061608174)(1001195)(4900095)(4920089)(6215004)(4950130)(4990090)(9110004); X-Message-Info: -5vMbyqxGkdfb86Tqlvb+ooiaK4miofpE0+yIiNadAlmGNL8EhFUXvmhvukIUGtSLIQDizKJRLr5qn0Y8t4RMcFg3r0i3VhrABjC9K0SzqKJTKwImhn5fIeRtcq/V1mtXjVbRQq/3vNoeUW/DfDQL1i2egaes+xB9Nl6Tl7oGfiVdQjbZJ0syQp1YJISxE2+CVn9X1+fIG4fVCleTXdKbtw== + 5vMbyqxGkdfb86Tqlvb+ooiaK4miofpE0+yIiNadAlmGNL8EhFUXvmhvukIUGtSLIQDizKJRLr5qn0Y8t4RMcFg3r0i3VhrABjC9K0SzqKJTKwImhn5fIeRtcq/V1mtXjVbRQq/3vNoeUW/DfDQL1i2egaes+xB9Nl6Tl7oGfiVdQjbZJ0syQp1YJISxE2+CVn9X1+fIG4fVCleTXdKbtw== X-Message-Delivery: Vj0xLjE7dXM9MDtsPTA7YT0xO0Q9MTtHRD0xO1NDTD0tMQ== X-Microsoft-Antispam-Message-Info: -XW0i+vGr98fR0H6N7ELZO09EwBcQq/HZi5UI2Nh7U1NT7S79GidnW3GK/KdcULFK1tmD/vRGElcMNUTtL/FK2YARRjxI0pEg64Iod8MyHBOw56UezwaZ7gwEYNBbT0TKq6O1dQLvIFx62Z3V7CG/bDG+zc+4ZEnWbmtIUbFgKwvtWj1DoLzxb+Y85UMqLKZpXNn8/U50Gfhv1mpZNum0q0IT2z7WLx1mNia4pWieIOLRQ9Lp/g7oNSL69tmrDXKFuz3fH4YQEs8uj/4FXRkLv/wGC13y8K8RnS7rd5UqFkOaTR8NV3ude3J328W6cx7X9XxKn0F6TP9Z8bNiMkLn/5RrYGnX0F4nkMqUYvjeEuF5b/0gvAU8UYWghnQCMB9aEx0XUBml2b3w/beLsMORvs4GBCi0orxJWMaQt6yOMMv+IuIgQZBma2bebhT14opkmt1zSmVuJAxJseixoYMsy+RXFGvSxuHwPgUEdBc0FzZKyu/N1sip/Y31SUhV5pFKid4TN5jCXjMHaEmAt4pVrf8vBi15SKopE6J7jXqzeYhxPpic4LFe58TVJUwUNpKe + XW0i+vGr98fR0H6N7ELZO09EwBcQq/HZi5UI2Nh7U1NT7S79GidnW3GK/KdcULFK1tmD/vRGElcMNUTtL/FK2YARRjxI0pEg64Iod8MyHBOw56UezwaZ7gwEYNBbT0TKq6O1dQLvIFx62Z3V7CG/bDG+zc+4ZEnWbmtIUbFgKwvtWj1DoLzxb+Y85UMqLKZpXNn8/U50Gfhv1mpZNum0q0IT2z7WLx1mNia4pWieIOLRQ9Lp/g7oNSL69tmrDXKFuz3fH4YQEs8uj/4FXRkLv/wGC13y8K8RnS7rd5UqFkOaTR8NV3ude3J328W6cx7X9XxKn0F6TP9Z8bNiMkLn/5RrYGnX0F4nkMqUYvjeEuF5b/0gvAU8UYWghnQCMB9aEx0XUBml2b3w/beLsMORvs4GBCi0orxJWMaQt6yOMMv+IuIgQZBma2bebhT14opkmt1zSmVuJAxJseixoYMsy+RXFGvSxuHwPgUEdBc0FzZKyu/N1sip/Y31SUhV5pFKid4TN5jCXjMHaEmAt4pVrf8vBi15SKopE6J7jXqzeYhxPpic4LFe58TVJUwUNpKe MIME-Version: 1.0 --ba016ad5-e34f-4e12-b45d-abfd610b3b50 Content-Type: multipart/alternative; differences=Content-Type; -boundary="e227aba6-3ba8-4338-af1b-ca67412ab125" + boundary="e227aba6-3ba8-4338-af1b-ca67412ab125" X-Microsoft-Exchange-Diagnostics: 1;SLXP216MB0381;27:EqSq7730xZ/A7XhFv+xXsKgzYSdQ9kKnrqzcIhDfytxdaNwbTqXgQavpBjlISujJD4flCzKYu/ShIqCtmSuuiPEJoA0ac/+819gW33695z67n+X/8mOvl/N60Puv0mND8DrbTTDGHCn7lWqKsEmD8Q== X-Microsoft-Antispam-Mailbox-Delivery: abwl:0;wl:0;pcwl:0;kl:0;iwl:0;dwl:0;dkl:0;rwl:0;ex:0;auth:1;dest:I;OFR:SpamFilterPass;ENG:(5062000261)(5061607266)(5061608174)(1001195)(4900095)(4920089)(6215004)(4950130)(4990090)(9110004); X-Message-Info: -5vMbyqxGkdfb86Tqlvb+ooiaK4miofpE0+yIiNadAlmGNL8EhFUXvmhvukIUGtSLIQDizKJRLr5qn0Y8t4RMcFg3r0i3VhrABjC9K0SzqKJTKwImhn5fIeRtcq/V1mtXjVbRQq/3vNoeUW/DfDQL1i2egaes+xB9Nl6Tl7oGfiVdQjbZJ0syQp1YJISxE2+CVn9X1+fIG4fVCleTXdKbtw== + 5vMbyqxGkdfb86Tqlvb+ooiaK4miofpE0+yIiNadAlmGNL8EhFUXvmhvukIUGtSLIQDizKJRLr5qn0Y8t4RMcFg3r0i3VhrABjC9K0SzqKJTKwImhn5fIeRtcq/V1mtXjVbRQq/3vNoeUW/DfDQL1i2egaes+xB9Nl6Tl7oGfiVdQjbZJ0syQp1YJISxE2+CVn9X1+fIG4fVCleTXdKbtw== X-Message-Delivery: Vj0xLjE7dXM9MDtsPTA7YT0xO0Q9MTtHRD0xO1NDTD0tMQ== X-Microsoft-Antispam-Message-Info: -XW0i+vGr98fR0H6N7ELZO09EwBcQq/HZi5UI2Nh7U1NT7S79GidnW3GK/KdcULFK1tmD/vRGElcMNUTtL/FK2YARRjxI0pEg64Iod8MyHBOw56UezwaZ7gwEYNBbT0TKq6O1dQLvIFx62Z3V7CG/bDG+zc+4ZEnWbmtIUbFgKwvtWj1DoLzxb+Y85UMqLKZpXNn8/U50Gfhv1mpZNum0q0IT2z7WLx1mNia4pWieIOLRQ9Lp/g7oNSL69tmrDXKFuz3fH4YQEs8uj/4FXRkLv/wGC13y8K8RnS7rd5UqFkOaTR8NV3ude3J328W6cx7X9XxKn0F6TP9Z8bNiMkLn/5RrYGnX0F4nkMqUYvjeEuF5b/0gvAU8UYWghnQCMB9aEx0XUBml2b3w/beLsMORvs4GBCi0orxJWMaQt6yOMMv+IuIgQZBma2bebhT14opkmt1zSmVuJAxJseixoYMsy+RXFGvSxuHwPgUEdBc0FzZKyu/N1sip/Y31SUhV5pFKid4TN5jCXjMHaEmAt4pVrf8vBi15SKopE6J7jXqzeYhxPpic4LFe58TVJUwUNpKe + XW0i+vGr98fR0H6N7ELZO09EwBcQq/HZi5UI2Nh7U1NT7S79GidnW3GK/KdcULFK1tmD/vRGElcMNUTtL/FK2YARRjxI0pEg64Iod8MyHBOw56UezwaZ7gwEYNBbT0TKq6O1dQLvIFx62Z3V7CG/bDG+zc+4ZEnWbmtIUbFgKwvtWj1DoLzxb+Y85UMqLKZpXNn8/U50Gfhv1mpZNum0q0IT2z7WLx1mNia4pWieIOLRQ9Lp/g7oNSL69tmrDXKFuz3fH4YQEs8uj/4FXRkLv/wGC13y8K8RnS7rd5UqFkOaTR8NV3ude3J328W6cx7X9XxKn0F6TP9Z8bNiMkLn/5RrYGnX0F4nkMqUYvjeEuF5b/0gvAU8UYWghnQCMB9aEx0XUBml2b3w/beLsMORvs4GBCi0orxJWMaQt6yOMMv+IuIgQZBma2bebhT14opkmt1zSmVuJAxJseixoYMsy+RXFGvSxuHwPgUEdBc0FzZKyu/N1sip/Y31SUhV5pFKid4TN5jCXjMHaEmAt4pVrf8vBi15SKopE6J7jXqzeYhxPpic4LFe58TVJUwUNpKe --e227aba6-3ba8-4338-af1b-ca67412ab125 Content-Type: text/plain; charset="us-ascii" @@ -140,10 +138,10 @@ X-Microsoft-Exchange-Diagnostics: X-Microsoft-Antispam-Mailbox-Delivery: abwl:0;wl:0;pcwl:0;kl:0;iwl:0;dwl:0;dkl:0;rwl:0;ex:0;auth:1;dest:I;OFR:SpamFilterPass;ENG:(5062000261)(5061607266)(5061608174)(1001195)(4900095)(4920089)(6215004)(4950130)(4990090)(9110004); X-Message-Info: -5vMbyqxGkdfb86Tqlvb+ooiaK4miofpE0+yIiNadAlmGNL8EhFUXvmhvukIUGtSLIQDizKJRLr5qn0Y8t4RMcFg3r0i3VhrABjC9K0SzqKJTKwImhn5fIeRtcq/V1mtXjVbRQq/3vNoeUW/DfDQL1i2egaes+xB9Nl6Tl7oGfiVdQjbZJ0syQp1YJISxE2+CVn9X1+fIG4fVCleTXdKbtw== + 5vMbyqxGkdfb86Tqlvb+ooiaK4miofpE0+yIiNadAlmGNL8EhFUXvmhvukIUGtSLIQDizKJRLr5qn0Y8t4RMcFg3r0i3VhrABjC9K0SzqKJTKwImhn5fIeRtcq/V1mtXjVbRQq/3vNoeUW/DfDQL1i2egaes+xB9Nl6Tl7oGfiVdQjbZJ0syQp1YJISxE2+CVn9X1+fIG4fVCleTXdKbtw== X-Message-Delivery: Vj0xLjE7dXM9MDtsPTA7YT0xO0Q9MTtHRD0xO1NDTD0tMQ== X-Microsoft-Antispam-Message-Info: -XW0i+vGr98fR0H6N7ELZO09EwBcQq/HZi5UI2Nh7U1NT7S79GidnW3GK/KdcULFK1tmD/vRGElcMNUTtL/FK2YARRjxI0pEg64Iod8MyHBOw56UezwaZ7gwEYNBbT0TKq6O1dQLvIFx62Z3V7CG/bDG+zc+4ZEnWbmtIUbFgKwvtWj1DoLzxb+Y85UMqLKZpXNn8/U50Gfhv1mpZNum0q0IT2z7WLx1mNia4pWieIOLRQ9Lp/g7oNSL69tmrDXKFuz3fH4YQEs8uj/4FXRkLv/wGC13y8K8RnS7rd5UqFkOaTR8NV3ude3J328W6cx7X9XxKn0F6TP9Z8bNiMkLn/5RrYGnX0F4nkMqUYvjeEuF5b/0gvAU8UYWghnQCMB9aEx0XUBml2b3w/beLsMORvs4GBCi0orxJWMaQt6yOMMv+IuIgQZBma2bebhT14opkmt1zSmVuJAxJseixoYMsy+RXFGvSxuHwPgUEdBc0FzZKyu/N1sip/Y31SUhV5pFKid4TN5jCXjMHaEmAt4pVrf8vBi15SKopE6J7jXqzeYhxPpic4LFe58TVJUwUNpKe + XW0i+vGr98fR0H6N7ELZO09EwBcQq/HZi5UI2Nh7U1NT7S79GidnW3GK/KdcULFK1tmD/vRGElcMNUTtL/FK2YARRjxI0pEg64Iod8MyHBOw56UezwaZ7gwEYNBbT0TKq6O1dQLvIFx62Z3V7CG/bDG+zc+4ZEnWbmtIUbFgKwvtWj1DoLzxb+Y85UMqLKZpXNn8/U50Gfhv1mpZNum0q0IT2z7WLx1mNia4pWieIOLRQ9Lp/g7oNSL69tmrDXKFuz3fH4YQEs8uj/4FXRkLv/wGC13y8K8RnS7rd5UqFkOaTR8NV3ude3J328W6cx7X9XxKn0F6TP9Z8bNiMkLn/5RrYGnX0F4nkMqUYvjeEuF5b/0gvAU8UYWghnQCMB9aEx0XUBml2b3w/beLsMORvs4GBCi0orxJWMaQt6yOMMv+IuIgQZBma2bebhT14opkmt1zSmVuJAxJseixoYMsy+RXFGvSxuHwPgUEdBc0FzZKyu/N1sip/Y31SUhV5pFKid4TN5jCXjMHaEmAt4pVrf8vBi15SKopE6J7jXqzeYhxPpic4LFe58TVJUwUNpKe Delivery has failed to these recipients or groups: diff --git a/set-of-emails/maildir/bsd/email-outlook-08.eml b/set-of-emails/maildir/bsd/email-outlook-08.eml index d58ca289..fb0f861f 100644 --- a/set-of-emails/maildir/bsd/email-outlook-08.eml +++ b/set-of-emails/maildir/bsd/email-outlook-08.eml @@ -31,7 +31,7 @@ Delivery to the following recipients failed. ---0022FFAB0000=_0000FFFF002222EE00220022220000?NEKO22.ho +--0022FFAB0000=_0000FFFF002222EE00220022220000?NEKO22.22 Content-Type: message/delivery-status Reporting-MTA: dns;BAY004-NEKO22.hotmail.com @@ -43,7 +43,7 @@ Action: failed Status: 5.5.0 Diagnostic-Code: smtp;550 Requested action not taken: mailbox unavailable (-0000000022:2222:-0000000022) ---0022FFAB0000=_0000FFFF002222EE00220022220000?NEKO22.ho +--0022FFAB0000=_0000FFFF002222EE00220022220000?NEKO22.22 Content-Type: message/rfc822 Received: from CAT004-NEKO0002.hotmail.com ([192.0.2.89]) by BAY004-NEKO22.hotmail.com @@ -67,3 +67,4 @@ Return-Path: sironeko@example.co.jp Nyaan +--0022FFAB0000=_0000FFFF002222EE00220022220000?NEKO22.22-- diff --git a/set-of-emails/maildir/bsd/email-verizon-02.eml b/set-of-emails/maildir/bsd/email-verizon-02.eml index 79f4a624..d03fa5b9 100644 --- a/set-of-emails/maildir/bsd/email-verizon-02.eml +++ b/set-of-emails/maildir/bsd/email-verizon-02.eml @@ -13,8 +13,7 @@ Message-ID: <00000000.111111.2222222222222.nyaaan@neko.example.com> In-Reply-To: <20140429222222.22222.qmail@sabineko.example.com> Subject: Re:Nyaan MIME-Version: 1.0 -Content-Type: multipart/mixed; -boundary="----=_B_00000000-213413246.adugzde" +Content-Type: multipart/mixed; boundary="----=_B_00000000-213413246.adugzde" Date: 29 Apr 2012 23:34:45 -0000 ------=_B_00000000-213413246.adugzde diff --git a/spec/sisimai/bite/email/private-apachejames_spec.rb b/spec/sisimai/bite/email/private-apachejames_spec.rb index a4183b0d..f6fb4699 100644 --- a/spec/sisimai/bite/email/private-apachejames_spec.rb +++ b/spec/sisimai/bite/email/private-apachejames_spec.rb @@ -5,8 +5,8 @@ { 'n' => '01001', 'r' => /filtered/ }, { 'n' => '01002', 'r' => /filtered/ }, { 'n' => '01003', 'r' => /filtered/ }, - { 'n' => '01004', 'r' => /undefined/ }, - { 'n' => '01005', 'r' => /undefined/ }, + { 'n' => '01004', 'r' => /onhold/ }, + { 'n' => '01005', 'r' => /onhold/ }, ] Sisimai::Bite::Email::Code.maketest(enginename, isexpected, true) diff --git a/spec/sisimai/bite/email/private-domino_spec.rb b/spec/sisimai/bite/email/private-domino_spec.rb index f797eb84..8fbcf6fc 100644 --- a/spec/sisimai/bite/email/private-domino_spec.rb +++ b/spec/sisimai/bite/email/private-domino_spec.rb @@ -15,7 +15,8 @@ { 'n' => '01011', 'r' => /userunknown/ }, { 'n' => '01012', 'r' => /userunknown/ }, { 'n' => '01013', 'r' => /userunknown/ }, - { 'n' => '01014', 'r' => /userunknown/ } + { 'n' => '01014', 'r' => /userunknown/ }, + { 'n' => '01015', 'r' => /networkerror/}, ] Sisimai::Bite::Email::Code.maketest(enginename, isexpected, true) diff --git a/spec/sisimai/bite/email/private-rfc3464_spec.rb b/spec/sisimai/bite/email/private-rfc3464_spec.rb index 8eb3e3c9..5fa473ec 100644 --- a/spec/sisimai/bite/email/private-rfc3464_spec.rb +++ b/spec/sisimai/bite/email/private-rfc3464_spec.rb @@ -249,7 +249,6 @@ { 'n' => '01261', 'r' => /policyviolation/ }, { 'n' => '01262', 'r' => /expired/ }, { 'n' => '01263', 'r' => /networkerror/ }, - { 'n' => '01264', 'r' => /networkerror/ }, { 'n' => '01265', 'r' => /policyviolation/ }, { 'n' => '01266', 'r' => /policyviolation/ }, { 'n' => '01267', 'r' => /hasmoved/ }, diff --git a/spec/sisimai/bite/email/public-domino_spec.rb b/spec/sisimai/bite/email/public-domino_spec.rb index c3768d28..46d17111 100644 --- a/spec/sisimai/bite/email/public-domino_spec.rb +++ b/spec/sisimai/bite/email/public-domino_spec.rb @@ -4,6 +4,7 @@ isexpected = [ { 'n' => '01', 's' => /\A5[.]0[.]\d+\z/, 'r' => /userunknown/, 'b' => /\A0\z/ }, { 'n' => '02', 's' => /\A5[.]0[.]\d+\z/, 'r' => /(?:userunknown|onhold)/, 'b' => /\d\z/ }, + { 'n' => '03', 's' => /\A5[.]0[.]\d+\z/, 'r' => /networkerror/, 'b' => /\A1\z/ }, ] Sisimai::Bite::Email::Code.maketest(enginename, isexpected) diff --git a/spec/sisimai/bite/email/public-rfc3464_spec.rb b/spec/sisimai/bite/email/public-rfc3464_spec.rb index 04a95ad6..6356c7eb 100644 --- a/spec/sisimai/bite/email/public-rfc3464_spec.rb +++ b/spec/sisimai/bite/email/public-rfc3464_spec.rb @@ -3,7 +3,6 @@ enginename = 'RFC3464' isexpected = [ { 'n' => '01', 's' => /\A5[.]1[.]1\z/, 'r' => /mailboxfull/, 'a' => /dovecot/, 'b' => /\A1\z/ }, - { 'n' => '02', 's' => /\A5[.]0[.]0\z/, 'r' => /networkerror/,'a' => /RFC3464/, 'b' => /\A1\z/ }, { 'n' => '03', 's' => /\A5[.]0[.]0\z/, 'r' => /policyviolation/,'a' => /RFC3464/, 'b' => /\A1\z/ }, { 'n' => '04', 's' => /\A5[.]5[.]0\z/, 'r' => /mailererror/, 'a' => /RFC3464/, 'b' => /\A1\z/ }, { 'n' => '05', 's' => /\A5[.]2[.]1\z/, 'r' => /filtered/, 'a' => /RFC3464/, 'b' => /\A1\z/ }, diff --git a/spec/sisimai/mime_spec.rb b/spec/sisimai/mime_spec.rb index cd61c465..8663e280 100644 --- a/spec/sisimai/mime_spec.rb +++ b/spec/sisimai/mime_spec.rb @@ -112,7 +112,7 @@ Reporting-MTA: dns; server-15.bemta-3.messagelabs.com Arrival-Date: Tue, 23 Dec 2014 20:39:34 +0000 - '; + ' context 'Quoted-Printable string' do it('returns "Neko"') { expect(cn.qprintd('=4e=65=6b=6f')).to be == 'Neko' } @@ -162,4 +162,104 @@ end end + describe '.makeflat' do + h9 = { 'content-type' => 'multipart/report; report-type=delivery-status; boundary="NekoNyaan--------1"' } + b9 = '--NekoNyaan--------1 +Content-Type: multipart/related; boundary="NekoNyaan--------2" + +--NekoNyaan--------2 +Content-Type: multipart/alternative; boundary="NekoNyaan--------3" + +--NekoNyaan--------3 +Content-Type: text/plain; charset="UTF-8" +Content-Transfer-Encoding: base64 + +c2lyb25la28K + +--NekoNyaan--------3 +Content-Type: text/html; charset="UTF-8" +Content-Transfer-Encoding: base64 + +PGh0bWw+CjxoZWFkPgogICAgPHRpdGxlPk5la28gTnlhYW48L3RpdGxlPgo8L2hl +YWQ+Cjxib2R5PgogICAgPGgxPk5la28gTnlhYW48L2gxPgo8L2JvZHk+CjwvaHRt +bD4K + +--NekoNyaan--------2 +Content-Type: image/jpg + +/9j/4AAQSkZJRgABAQEBLAEsAAD/7VaWUGhvdG9zaG9wIDMuMAA4QklNBAwAAAAA +Vk4AAAABAAAArwAAAQAAAAIQAAIQAAAAVjIAGAAB/9j/7gAOQWRvYmUAZAAAAAAB +/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDAwMDAwM +DAwMDAwMDAwMDAwMDAwMDAwMDAwMAQcHBw0MDRgQEBgUDg4OFBQODg4OFBEMDAwM +DBERDAwMDAwMEQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAEAAK8D +AREAAhEBAxEB/90ABAAW/8QBogAAAAcBAQEBAQAAAAAAAAAABAUDAgYBAAcICQoL + +--NekoNyaan--------2 +Content-Type: message/delivery-status + +Reporting-MTA: dns; example.jp +Received-From-MTA: dns; neko.example.jp +Arrival-Date: Thu, 11 Oct 2018 23:34:45 +0900 (JST) + +Final-Recipient: rfc822; kijitora@example.jp +Action: failed +Status: 5.1.1 +Diagnostic-Code: User Unknown + +--NekoNyaan--------2 +Content-Type: message/rfc822 + +Received: ... + +--NekoNyaan--------2-- + + ' + context 'mutipart/report message body' do + p9 = cn.makeflat(h9['content-type'], b9) + it('returns String') { expect(p9).to be_a String } + it('contain "text/plain part"') { expect(p9).to match(/sironeko/) } + it('does not contain text/html part') { expect(p9).not_to match(//) } + it('does not contain image/jpg part') { expect(p9).not_to match(/4AAQSkZJRgABAQEBLAEsAAD/) } + it('contain "message/delivery-status part"') { expect(p9).to match(/kijitora[@]/) } + it('contain "message/rfc822 part"') { expect(p9).to match(/Received:/) } + it('returns Nil') { expect(cn.makeflat(nil,nil)).to be_nil } + end + + context 'wrong number of arguments' do + it('raises ArgumentError') { expect { cn.makeflat(nil,nil,nil) }.to raise_error(ArgumentError) } + end + end + + describe '.breaksup' do + h10 = 'multipart/alternative' + b10 = 'Content-Type: multipart/alternative; boundary="NekoNyaan--------3" + +--NekoNyaan--------3 +Content-Type: text/plain; charset="UTF-8" +Content-Transfer-Encoding: base64 + +c2lyb25la28K + +--NekoNyaan--------3 +Content-Type: text/html; charset="UTF-8" +Content-Transfer-Encoding: base64 + +PGh0bWw+CjxoZWFkPgogICAgPHRpdGxlPk5la28gTnlhYW48L3RpdGxlPgo8L2hl +YWQ+Cjxib2R5PgogICAgPGgxPk5la28gTnlhYW48L2gxPgo8L2JvZHk+CjwvaHRt +bD4K + ' + context 'mutipart/alternative part' do + p10 = cn.breaksup(b10, h10) + it('returns String') { expect(p10).to be_a String } + it('contain "text/plain part"') { expect(p10).to match(/sironeko/) } + it('does not contain text/html part') { expect(p10).not_to match(//) } + it('returns Nil') { expect(cn.breaksup(nil,nil)).to be_nil } + end + + context 'wrong number of arguments' do + it('raises ArgumentError') { expect { cn.makeflat(nil,nil,nil) }.to raise_error(ArgumentError) } + end + + end + end diff --git a/spec/sisimai/rfc2606_spec.rb b/spec/sisimai/rfc2606_spec.rb deleted file mode 100644 index 5492c15a..00000000 --- a/spec/sisimai/rfc2606_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'spec_helper' -require 'sisimai/rfc2606' - -describe Sisimai::RFC2606 do - cn = Sisimai::RFC2606 - arerfc2606 = ['example.jp', 'example.com', 'example.org', 'example.net'] - notrfc2606 = ['bouncehammer.jp', 'cubicroot.jp', 'gmail.com', 'me.com'] - - describe '.is_reserved' do - context 'valid domain string' do - arerfc2606.each do |e| - context "(#{e})" do - it('returns true') { expect(cn.is_reserved(e)).to be true } - end - end - - notrfc2606.each do |e| - context "(#{e})" do - it('returns false') { expect(cn.is_reserved(e)).to be false } - end - end - end - - context 'valid email address string' do - arerfc2606.each do |e| - context "(neko@#{e})" do - it('returns true') { expect(cn.is_reserved('neko@' + e)).to be true } - end - end - - notrfc2606.each do |e| - context "(example.jp@#{e})" do - it('returns false') { expect(cn.is_reserved('example.jp@' + e)).to be false } - end - end - end - - context 'not string' do - context '(2)' do - it('returns false') { expect(cn.is_reserved(2)).to be false } - end - context '(nil)' do - it('returns false') { expect(cn.is_reserved(nil)).to be false } - end - end - - context 'wrong number of arguments' do - context '("x","y")' do - it('raises ArgumentError') { expect { cn.is_reserved('x', 'y') }.to raise_error(ArgumentError) } - end - end - - end -end