#!/usr/local/bin/ruby # # convert Postfix log to CSV text # $Id: pflog,v 1.6 2003/12/10 15:20:05 tommy Exp $ # # Usage: pflog [option] log-filename... # -d output unknown format to stderr # -Y yyyy set year # -s filename save information to filename # # output: # queue id # arrived time # processed time # smtp client hostname / uid # smtp client IP address / username # envelope from # envelope to # message-id # status # relay to # delay time # size # information (reason of defered, local mailbox name, successful message...) require "getopts" require "dbm" $keep_message_info = 7*24*60*60 $mon = [0, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] class MsgInfo def initialize(filename) @dbm = DBM::open(filename) end def [](q) @dbm[q] ? Marshal::load(@dbm[q]) : nil end def []=(q, v) @dbm[q] = Marshal::dump v end def clear(time) @dbm.delete_if do |q,v| m = Marshal::load(v) not m.key?(:arrive_time) or m[:arrive_time] < time end end end if getopts('d', 'Y:', 's:') == nil then $stderr.puts <>>>>>>>" if $debug end def csvout(*values) puts values.map{|v| v.to_s.include?(",") ? "\"#{v.gsub(/\"/,'""')}\"" : v}.join(",") end first = true ARGF.each do |line| line.chomp! if first then if line =~ /^([A-Z][a-z][a-z] ?\d+ \d+:\d+:\d+)/ then if Time::now < toTime($1) then $year -= 1 end $msginfo.clear(toTime($1)-$keep_message_info) if $msginfo.is_a? MsgInfo first = false end end unless line =~ /^([A-Z][a-z][a-z] ?\d+ \d+:\d+:\d+) (\S+) postfix\/\w+\[\d+\]: (?:\[[^\]]+\] )?(.*)$/ unknown_format line next end datetime = $1 hostname = $2 c = $3 next if c =~ /^(dis)?connect from / unless c =~ /^([A-Z0-9]+): (.*)$/ unknown_format line next end id = $1 c = $2 if $msginfo[id] then h = $msginfo[id] else h = {} end case c when /^client=(\S+)\[([^\]]*)\]/ h[:arrive_time] = toTime(datetime) h[:hostname] = $1 h[:ipaddr] = $2 when /^uid=(\d+) from=<(.*)>/ h[:arrive_time] = toTime(datetime) h[:hostname] = $2 h[:ipaddr] = $1 when /^message-id=(.*)/ h[:msgid] = $1 when /^from=<(.*)>, size=(\d+),/ h[:from] = $1 h[:size] = $2.to_i when /^to=<(.*?)>, (?:orig_to=<.*>, )?relay=(.*), delay=(\d+), status=(\S+) \((.*)\)/ to, relay, delay, status, info = $1, $2, $3, $4, $5 action_time = toTime(datetime).strftime("%Y/%m/%d %H:%M:%S") at = h[:arrive_time] ? h[:arrive_time].strftime("%Y/%m/%d %H:%M:%S") : "" csvout id,at,action_time,h[:hostname],h[:ipaddr],h[:from],to,h[:msgid],status,relay,delay,h[:size],info when /^reject: .* from (.*)\[([^\]]+)\]: .*; from=<(.*?)> to=<(.*?)>/ hostname, ipaddr, from, to = $1, $2, $3, $4 status = "reject" info = c action_time = toTime(datetime).strftime("%Y/%m/%d %H:%M:%S") at = h[:arrive_time] ? h[:arrive_time].strftime("%Y/%m/%d %H:%M:%S") : "" csvout id,at,action_time,hostname,ipaddr,from,to,"",status,"","","",info else unknown_format line end $msginfo[id] = h end