#!/usr/bin/ruby

require 'pcap_tools'
require 'optparse'

OPTIONS = {
  :mode => :http,
}

OptionParser.new do |opts|
  opts.banner = "Usage: pcap_tools_http [options] pcap_files"

  opts.on("--no-body", "Do not display body") do
    OPTIONS[:no_body] = true
  end

  opts.on("--tshark_path", "Path to tshark executable") do |x|
    OPTIONS[:tshark] = x
  end

  opts.on("--one-tcp-stream [index]", Integer, "Display only one tcp stream") do |x|
    OPTIONS[:one_tcp_stream] = x
  end

  opts.on("--mode [MODE]", [:http, :tcp, :frame, :tcp_count], "Parsing mode : http, tcp, frame, tcp_count. Default http") do |m|
    OPTIONS[:mode] = m
  end

  opts.on("--pdml", "Use pdml file as input instead of pcap") do |x|
    OPTIONS[:pdml] = true
  end

  opts.on("--keep_retransmission", "Do not ignore retransmitted packets") do |x|
    OPTIONS[:keep_retransmission] = true
  end

end.parse!

def format_time t
  "#{t} #{t.nsec / 1000}"
end

puts "Mode : #{OPTIONS[:mode]}"

processor = nil

if OPTIONS[:mode] == :frame

  processor = PcapTools::FrameProcessor.new

else

  class TcpCounter

    def initialize
      @counter = 0
    end

    def process_stream stream
      @counter += 1
      stream
    end

    def finalize
      puts "Number of TCP Streams : #{@counter}"
    end

  end


  processor = PcapTools::TcpProcessor.new
  processor.add_stream_processor TcpCounter.new
  processor.add_stream_processor PcapTools::TcpOneStreamFilter.new OPTIONS[:one_tcp_stream]

  if OPTIONS[:mode] == :tcp

    class TcpPrinter

      def process_stream stream
        puts "<<<< new connection >>>> [ Wirershark stream index #{stream[:index]} ]"
        stream[:data].each do |packet|
          type = packet[:type] == :out ? ">>>>" : "<<<<<"
          puts "#{type} #{packet[:from]}:#{packet[:from_port]} > #{packet[:to]}:#{packet[:to_port]}, size #{packet[:data].size}  #{format_time packet[:time]}"
          puts packet[:data] unless OPTIONS[:no_body]
          puts
        end
      end

      def finalize
      end

    end

    processor.add_stream_processor TcpPrinter.new

  end

  if OPTIONS[:mode] == :http

    class HttpPrinter

      def initialize
        @counter = 0
      end

      def process_stream stream
        stream.each do |index, req, resp|
          @counter += 1
          puts ">>>> #{req["pcap-src"]}:#{req["pcap-src-port"]} > #{req["pcap-dst"]}:#{req["pcap-dst-port"]} #{format_time req.time} [ Wirershark stream index #{index} ]"
          puts "#{req.method} #{req.path}"
          req.each_capitalized_name.reject{|x| x =~ /^Pcap/ }.each do |x|
            puts "#{x}: #{req[x]}"
          end
          puts
          puts req.body unless OPTIONS[:no_body]
          if resp
            puts "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #{format_time resp.time}"
            puts "#{resp.code} #{resp.message}"
            resp.each_capitalized_name.reject{|x| x =~ /^Pcap/ }.each do |x|
              puts "#{x}: #{resp[x]}"
            end
            puts
            puts resp.body unless OPTIONS[:no_body]
          else
            puts "No response found"
          end
          puts
        end
      end

      def finalize
        puts "Number of HTTP Request / response : #{@counter}"
      end

    end

    processor.add_stream_processor PcapTools::TcpStreamRebuilder.new
    processor.add_stream_processor PcapTools::HttpExtractor.new
    processor.add_stream_processor HttpPrinter.new

  end

end

ARGV.each do |f|
  PcapTools::Loader::load_file(f, OPTIONS) do |index, packet|
    processor.inject index, packet
  end
end

processor.finalize
