#!/usr/bin/env ruby

$LOAD_PATH.unshift *Dir["#{File.dirname(__FILE__)}/../lib"]
APP_ROOT = "#{File.dirname(__FILE__)}/.."

require "rubygems"
require "proxy"
require "sinatra-patch"
require "json"
require "proxy/log"
require "helpers"

class SmartProxy < Sinatra::Base
  attr_reader :ssl_options

  include Proxy::Log
  require 'helpers'

  set :root, APP_ROOT
  set :views, APP_ROOT + '/views'
  set :public, APP_ROOT + '/public'
  set :logging, true
  set :env,     :production
  set :run,     true

  require "tftp_api"     if SETTINGS.tftp
  require "puppet_api"   if SETTINGS.puppet
  require "puppetca_api" if SETTINGS.puppetca
  require "dns_api"      if SETTINGS.dns
  require "dhcp_api"     if SETTINGS.dhcp
  require "features_api"

  # we force webrick to allow SSL
  set :server, "webrick"
  set :port, SETTINGS.port if SETTINGS.port

  # SSL Setup
  unless SETTINGS.ssl_private_key and SETTINGS.ssl_certificate and SETTINGS.ssl_ca_file
    warn "WARNING: Missing SSL setup, working in clear text mode !\n"
    @ssl_options = {}
  else
    begin
      @ssl_options = {:SSLEnable => true,
        :SSLVerifyClient      => OpenSSL::SSL::VERIFY_PEER,
        :SSLPrivateKey        => OpenSSL::PKey::RSA.new(File.read(SETTINGS.ssl_private_key)),
        :SSLCertificate       => OpenSSL::X509::Certificate.new(File.read(SETTINGS.ssl_certificate)),
        :SSLCACertificateFile => SETTINGS.ssl_ca_file
      }
    rescue => e
      warn "Unable to access the SSL keys. Are the values correct in settings.yml and do permissions allow reading?: #{e}"
      exit 1
    end
  end

  before do
    # If we reach here then the peer is verified and cannot be spoofed
    if ssl_options and SETTINGS.trusted_hosts
      unless SETTINGS.trusted_hosts.include? request.env["REMOTE_HOST"].downcase
        log_halt 403, "Untrusted client #{request.env["REMOTE_HOST"].downcase} attempted to access #{request.path_info}. Check :trusted_hosts: in settings.yml"
      end
    end
  end
end

if ARGV[0] == "--service"
  raise "The service flag is used only in a windows environment" unless PLATFORM =~ /mingw/
  begin
    require 'win32/daemon'
    include Win32

    # Logfile must be absolute on windows
    logfile = (SETTINGS.log_file =~ /^\\|^[a-z]:/i) ? SETTINGS.log_file : File.dirname(__FILE__) + "\\..\\#{SETTINGS.log_file}"
    $stdout.reopen(logfile, "a")
    $stdout.sync = true
    $stderr.reopen($stdout)
    puts "#{Time.now}: Service is starting"

    class Daemon
      def service_init
        puts "#{Time.now}: Service is initializing"
      end

      def service_main(*args)
        puts "#{Time.now}: Service is running"
        SmartProxy.run!()
        puts "#{Time.now}: Service is terminating"
      end

      def service_stop
        puts "#{Time.now}: Service stopped"
        exit!
      end
    end

    daemon = Daemon.new
    daemon.mainloop
  rescue Exception => err
    File.open(logfile, (File::APPEND|File::CREAT|File::WRONLY)){ |f| f.puts " ***Daemon failure #{Time.now} err=#{err}" }
    raise
  end
else
  SmartProxy.run!()
end
