Module: Sisimai::SMTP::Transcript
- Defined in:
- lib/sisimai/smtp/transcript.rb
Overview
Sisimai::SMTP::Transcript is a decoder for the transcript logs of the SMTP session
Class Method Summary collapse
-
.rise(argv0 = '', argv1 = '>>>', argv2 = '<<<') ⇒ Array
abstract
Structured data [Nil] Failed to decode or the 1st argument is missing.
Class Method Details
.rise(argv0 = '', argv1 = '>>>', argv2 = '<<<') ⇒ Array
This method is abstract.
decodes the transcript of the SMTP session and makes structured data
Returns Structured data
- Nil
-
Failed to decode or the 1st argument is missing.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/sisimai/smtp/transcript.rb', line 16 def rise(argv0 = '', argv1 = '>>>', argv2 = '<<<') return nil if argv0.size == 0 # 1. Replace label strings of SMTP client/server at the each line argv0.gsub!(/^[ ]+#{argv1}\s+/m, '>>> '); return nil if argv0.include?('>>> ') == false argv0.gsub!(/^[ ]+#{argv2}\s+/m, '<<< '); return nil if argv0.include?('<<< ') == false # 2. Remove strings until the first '<<<' or '>>>' esmtp = [] table = lambda do return { 'command' => nil, # SMTP command 'argument' => '', # An argument of each SMTP command sent from a client 'parameter' => {}, # Parameter pairs of the SMTP command 'response' => { # A Response from an SMTP server 'reply' => '', # - SMTP reply code such as 550 'status' => '', # - SMTP status such as 5.1.1 'text' => [], # - Response text lines } } end cv = '' cx = nil # Current session for esmtp p1 = argv0.index('>>>') || -1 # Sent command p2 = argv0.index('<<<') || -1 # Server response if p2 < p1 # An SMTP server response starting with '<<<' is the first esmtp << table.call cx = esmtp[-1] cx['command'] = 'CONN' argv0 = argv0[p2, argv0.size] if p2 > -1 else # An SMTP command starting with '>>>' is the first argv0 = argv0[p1, argv0.size] if p1 > -1 end # 3. Remove unused lines, concatenate folded lines argv0 = argv0[0, argv0.index("\n\n") - 1] # Remove strings from the first blank line to the tail argv0.gsub!(/\n[ ]+/m, ' ') # Concatenate folded lines to each previous line argv0.split("\n").each do |e| # 4. Read each SMTP command and server response if e.start_with?('>>> ') # SMTP client sent a command ">>> SMTP-command arguments" if cv = e.match(/\A>>>[ ]([A-Z]+)[ ]?(.*)\z/) # >>> SMTP Command thecommand = cv[1] commandarg = cv[2] parameters = '' esmtp << table.call cx = esmtp[-1] cx['command'] = thecommand.upcase if thecommand =~ /\A(?:MAIL|RCPT|XFORWARD)/ # MAIL or RCPT if cv = commandarg.match(/\A(?:FROM|TO):[ ]*<(.+[@].+)>[ ]*(.*)\z/) # >>> MAIL FROM: <neko@example.com> SIZE=65535 # >>> RCPT TO: <kijitora@example.org> cx['argument'] = cv[1] parameters = cv[2] else # >>> XFORWARD NAME=neko2-nyaan3.y.example.co.jp ADDR=230.0.113.2 PORT=53672 # <<< 250 2.0.0 Ok # >>> XFORWARD PROTO=SMTP HELO=neko2-nyaan3.y.example.co.jp IDENT=2LYC6642BLzFK3MM SOURCE=REMOTE # <<< 250 2.0.0 Ok parameters = commandarg commandarg = '' end parameters.split(" ").each do |p| # SIZE=22022, PROTO=SMTP, and so on if cv = p.match(/\A([^ =]+)=([^ =]+)\z/) then cx['parameter'][cv[1].downcase] = cv[2] end end else # HELO, EHLO, AUTH, DATA, QUIT or Other SMTP command cx['argument'] = commandarg end end else # SMTP server sent a response "<<< response text" p = e.index('<<< '); next if p.nil? && p != 0 e = e[4, e.size].sub(/\A<<<[ ]/, '') if cv = e.match(/\A([2-5]\d\d)[ ]/) then cx['response']['reply'] = cv[1] end if cv = e.match(/\A[245]\d\d[ ]([245][.]\d{1,3}[.]\d{1,3})[ ]/) then cx['response']['status'] = cv[1] end cx['response']['text'] << e end end return nil if esmtp.size == 0 return esmtp end |