#!/usr/bin/env ruby
#  Phusion Passenger - https://www.phusionpassenger.com/
#  Copyright (c) 2010-2013 Phusion
#
#  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#  THE SOFTWARE.

## Magic comment: set locations.ini ##

PASSENGER_ROOT = File.expand_path(File.dirname(__FILE__) + "/..")
$LOAD_PATH.unshift("#{PASSENGER_ROOT}/lib")
require 'phusion_passenger'
PhusionPassenger.locate_directories

# The Apache executable may be located in an 'sbin' folder. We add
# the 'sbin' folders to $PATH just in case. On some systems
# 'sbin' isn't in $PATH unless the user is logged in as root from
# the start (i.e. not via 'su' or 'sudo').
ENV["PATH"] += ":/usr/sbin:/sbin:/usr/local/sbin"

require 'optparse'
require 'phusion_passenger/platform_info/ruby'
require 'phusion_passenger/platform_info/apache'
require 'phusion_passenger/abstract_installer'

class Installer < PhusionPassenger::AbstractInstaller
	include PhusionPassenger
	
	def dependencies
		specs = [
			'depcheck_specs/compiler_toolchain',
			'depcheck_specs/ruby',
			'depcheck_specs/gems',
			'depcheck_specs/libs',
			'depcheck_specs/apache2'
		]
		ids = [
			'gcc',
			'g++',
			'libcurl-dev',
			'openssl-dev',
			'zlib-dev',
			PlatformInfo.passenger_needs_ruby_dev_header? ? 'ruby-dev' : nil,
			'ruby-openssl',
			'rubygems',
			'rake',
			'rack',
			'apache2',
			'apache2-dev'
		].compact
		# Some broken servers don't have apr-config or apu-config installed.
		# Nevertheless, it is possible to compile Apache modules if Apache
		# was configured with --included-apr. So here we check whether
		# apr-config and apu-config are available. If they're not available,
		# then we only register them as required dependency if no Apache
		# module can be compiled without their presence.
		if (PlatformInfo.apr_config && PlatformInfo.apu_config) ||
		   PlatformInfo.apr_config_needed_for_building_apache_modules?
			ids << 'apr-dev'
			ids << 'apu-dev'
		end
		return [specs, ids]
	end
	
	def users_guide
		return "#{PhusionPassenger.doc_dir}/Users guide Apache.html"
	end
	
	def run_steps
		if PhusionPassenger.natively_packaged?
			check_dependencies || exit(1)
			show_apache2_config_snippets
			show_deployment_example
			exit
		end
		
		Dir.chdir(PASSENGER_ROOT)
		show_welcome_screen
		check_dependencies || exit(1)
		check_whether_apache_uses_compatible_mpm
		check_whether_os_is_broken
		check_whether_system_has_enough_ram
		check_write_permission_to_passenger_root || exit(1)
		if install_apache2_module
			show_apache2_config_snippets
			show_deployment_example
		else
			show_possible_solutions_for_compilation_and_installation_problems
			exit(1)
		end
	end

private
	def show_welcome_screen
		render_template 'apache2/welcome', :version => VERSION_STRING
		wait
	end
	
	def check_whether_apache_uses_compatible_mpm
		# 'httpd -V' output is in the form of:
		#
		# Server MPM:      Prefork     # <--- this line is not always available!
		# ...
		# Server compiled with....
		#  -D APACHE_MPM_DIR="server/mpm/prefork"
		output = `#{PlatformInfo.httpd} -V`
		output =~ /^Server MPM: +(.*)$/
		if $1
			mpm = $1.downcase
		else
			output =~ /APACHE_MPM_DIR="server\/mpm\/(.*)"/
			if $1
				mpm = $1.downcase
			else
				mpm = nil
			end
		end
		if mpm != "prefork" && mpm != "worker" && mpm != "event"
			new_screen
			render_template 'apache2/apache_must_be_compiled_with_compatible_mpm',
				:current_mpm => mpm
			wait
		end
	end
	
	def check_write_permission_to_passenger_root
		File.new("__test__.txt", "w").close
		return true
	rescue SystemCallError
		puts
		line
		if Process.uid == 0
			render_template 'apache2/no_write_permission_to_passenger_root'
		else
			render_template 'installer_common/run_installer_as_root',
				:dir  => PhusionPassenger.source_root,
				:sudo => PhusionPassenger::PlatformInfo.ruby_sudo_command,
				:sudo_e => "#{PhusionPassenger::PlatformInfo.ruby_sudo_command} -E",
				:ruby => PhusionPassenger::PlatformInfo.ruby_command,
				:installer => "#{PhusionPassenger.bin_dir}/passenger-install-apache2-module #{ORIG_ARGV.join(' ')}"
		end
		return false
	ensure
		File.unlink("__test__.txt") rescue nil
	end
	
	def install_apache2_module
		puts
		line
		puts '<banner>Compiling and installing Apache 2 module...</banner>'
		puts "cd #{PASSENGER_ROOT}"
		if ENV['TRACE']
			puts "#{PlatformInfo.rake_command} --trace apache2:clean apache2 RELEASE=yes"
			return sh("#{PlatformInfo.rake_command} --trace apache2:clean apache2 RELEASE=yes")
		else
			puts "#{PlatformInfo.rake_command} apache2:clean apache2 RELEASE=yes"
			return sh("#{PlatformInfo.rake_command} apache2:clean apache2 RELEASE=yes")
		end
	end
	
	def show_apache2_config_snippets(bare = false)
		if bare
			puts "LoadModule passenger_module #{PhusionPassenger.apache2_module_path}"
			puts "PassengerRoot #{PhusionPassenger.source_root}"
			puts "PassengerDefaultRuby #{PlatformInfo.ruby_command}"
		else
			puts
			line
			render_template 'apache2/config_snippets',
				:module_location => PhusionPassenger.apache2_module_path,
				:passenger_root => PhusionPassenger.source_root,
				:ruby => PlatformInfo.ruby_command
			if PhusionPassenger.originally_packaged?
				wait
			else
				wait(10)
			end
		end
	end
	
	def show_deployment_example
		puts
		line
		render_template 'apache2/deployment_example',
			:users_guide => users_guide,
			:phusion_website => PHUSION_WEBSITE,
			:passenger_website => PASSENGER_WEBSITE
	end
	
	def show_possible_solutions_for_compilation_and_installation_problems
		new_screen
		render_template 'apache2/possible_solutions_for_compilation_and_installation_problems',
			:users_guide => users_guide,
			:passenger_website => PASSENGER_WEBSITE
	end
end

ORIG_ARGV = ARGV.dup
options = {}
parser = OptionParser.new do |opts|
	opts.banner = "Usage: passenger-install-apache2-module [options]"
	opts.separator ""
	
	opts.separator "Options:"
	opts.on("-a", "--auto", String, "Automatically build the Apache module,\n" <<
	        "#{' ' * 37}without interactively asking for user\n" <<
	        "#{' ' * 37}input.") do
		options[:auto] = true
	end
	opts.on("--apxs2-path PATH", String, "Path to 'apxs2' command.") do |value|
		ENV['APXS2'] = value
	end
	opts.on("--apr-config-path PATH", String, "Path to 'apr-config' command.") do |value|
		ENV['APR_CONFIG'] = value
	end
	opts.on("--snippet", "Show just the Apache config snippet.") do
		options[:snippet] = true
	end
end
begin
	parser.parse!
rescue OptionParser::ParseError => e
	puts e
	puts
	puts "Please see '--help' for valid options."
	exit 1
end

installer = Installer.new(options)
if options[:snippet]
	installer.send(:show_apache2_config_snippets, true)
else
	installer.run
end
