# encoding: utf-8
#  Phusion Passenger - https://www.phusionpassenger.com/
#  Copyright (c) 2014 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.

PhusionPassenger.require_passenger_lib 'constants'
PhusionPassenger.require_passenger_lib 'config/command'

module PhusionPassenger
module Config

class ValidateInstallCommand < Command
	# Signifies that there is at least 1 error.
	FAIL_EXIT_CODE = 1
	# Signifies that there are no error, but at least 1 warning.
	WARN_EXIT_CODE = 2

	def self.help
		puts "Usage: passenger-config validate-install"
		puts "Validate this #{PROGRAM_NAME} installation."
		puts
		puts "Exit codes:"
		puts "  0 - All checks passed. No errors, no warnings."
		puts "  #{FAIL_EXIT_CODE} - There are some errors."
		puts "  #{WARN_EXIT_CODE} - There are no errors, but there are some warnings."
	end

	def run
		if @argv[0] == '--help'
			self.class.help
			exit
		elsif @argv.size > 0
			self.class.help
			exit 1
		end

		begin
			require 'rubygems'
		rescue LoadError
		end
		PhusionPassenger.require_passenger_lib 'utils/ansi_colors'
		PhusionPassenger.require_passenger_lib 'platform_info'

		@error_count = 0
		@warning_count = 0

		prepare_terminal
		begin
			check_tools_in_path
			check_no_other_installs_in_path

			exit(FAIL_EXIT_CODE) if @error_count > 0
			exit(WARN_EXIT_CODE) if @warning_count > 0
		ensure
			reset_terminal
		end
	end

private
	def prepare_terminal
		STDOUT.write(Utils::AnsiColors::DEFAULT_TERMINAL_COLOR)
		STDOUT.flush
	end

	def reset_terminal
		STDOUT.write(Utils::AnsiColors::RESET)
		STDOUT.flush
	end

	def check_tools_in_path
		checking "whether this #{PROGRAM_NAME} install is in PATH"
		paths = ENV['PATH'].to_s.split(':')
		if paths.include?(gem_bindir) ||
		   paths.include?(homebrew_bindir) ||
		   paths.include?(PhusionPassenger.bin_dir)
			check_ok
		else
			check_warning
			suggest %Q{
				Please add #{PhusionPassenger.bin_dir} to PATH.
				Otherwise you will get "command not found" errors upon running
				passenger-status and other tools.

				Learn more at about PATH at:

				  #{NGINX_DOC_URL}#_the_path_environment_variable
			}
		end
	end

	def check_no_other_installs_in_path
		logn " * Checking whether there are no other #{PROGRAM_NAME} installations... "

		paths = ENV['PATH'].to_s.split(':')
		if Process.uid == 0 &&
		   (sudo_user = ENV['SUDO_USER']) &&
		   (bash = PlatformInfo.find_command("bash")) &&
		   PlatformInfo.find_command("sudo")
			# If we were invoked through sudo then we need to check the original user's PATH too.
			output = `sudo -u #{sudo_user} #{bash} -lc 'echo; echo PATH FOLLOWS; echo "$PATH"' 2>&1`
			output.sub!(/.*\nPATH FOLLOWS\n/m, '')
			output.strip!
			paths.concat(output.split(':'))
		end

		paths.delete(gem_bindir)
		paths.delete(homebrew_bindir)
		paths.delete(PhusionPassenger.bin_dir)
		# These may not be in PATH if the user did not run this command through sudo.
		paths << "/usr/bin"
		paths << "/usr/sbin"
		paths.uniq!

		other_installs = []
		paths.each do |path|
			filename = "#{path}/passenger-config"
			if File.exist?(filename)
				other_installs << filename
			end
		end
		if other_installs.empty?
			check_ok
		else
			check_warning
			suggest %Q{
				Besides this #{PROGRAM_NAME} installation, the following other
				#{PROGRAM_NAME} installations have been detected:

				  #{other_installs.join("\n\t\t\t\t  ")}

				Please uninstall them to avoid confusion or conflicts.
			}
		end
	end

	# Returns the RubyGems bin dir, if Phusion Passenger is installed through RubyGems.
	def gem_bindir
		if defined?(Gem) &&
		   PhusionPassenger.originally_packaged? &&
		   PhusionPassenger.source_root =~ /^#{Regexp.escape Gem.dir}\// &&
		   File.exist?("#{Gem.bindir}/passenger-config")
			return Gem.bindir
		else
			return nil
		end
	end

	# Returns the Homebrew bin dir, if Phusion Passenger is installed through Homebrew.
	def homebrew_bindir
		if PhusionPassenger.native_packaging_method == "homebrew"
			return "/usr/local/bin"
		else
			return nil
		end
	end

	def logn(message)
		if STDOUT.tty?
			STDOUT.write(Utils::AnsiColors.ansi_colorize(message))
		else
			STDOUT.write(Utils::AnsiColors.strip_color_tags(message))
		end
		STDOUT.flush
	end

	def log(message)
		if STDOUT.tty?
			STDOUT.puts(Utils::AnsiColors.ansi_colorize(message))
		else
			STDOUT.puts(Utils::AnsiColors.strip_color_tags(message))
		end
	end

	def checking(message)
		logn " * Checking #{message}... "
	end

	def check_ok(message = "✓")
		log "<green>#{message}</green>"
	end

	def check_error(message = "✗")
		log "<red>#{message}</red>"
		@error_count += 1
	end

	def check_warning(message = "(!)")
		log "<yellow>#{message}</yellow>"
		@warning_count += 1
	end

	def suggest(message)
		puts
		log reindent(unindent(message), 3)
		puts
	end

	def unindent(text)
		return PlatformInfo.send(:unindent, text)
	end

	def reindent(text, level)
		return PlatformInfo.send(:reindent, text, level)
	end
end

end # module Config
end # module PhusionPassenger
