#!/usr/bin/env ruby

#
# tdiary-setup --- tDiary Setup Tool
# Author: Daigo Moriwaki <beatles@sgtpepper.net>
# Copyright (c) 2004 Daigo Moriwaki
# License: GPLv2 
#

require 'fileutils'
require 'webrick/httpauth/htpasswd'  
require 'debian'

#
# Extend WEBrick
#
class WEBrick::HTTPAuth::Htpasswd
   def path
      @path
	end
end
	

module TDiarySetup
	# Directory where tdiary is installed on Debian
   TDIARY_DIR = "/usr/share/tdiary"
	
	#
	# Hold parameters to be used on installation. 
	#
   class Parameters
	   attr_accessor :user, :home, :data_path, :language
      def initialize(user = ENV['USER'], home = ENV['HOME']) 
         @user = user
			@home = home
			@language  = "E"
			@data_path = "#@home/data"
			@target    = "#@home/public_html/diary"
		end

		def language_type
         case @language
			when /e/i
				:en
			when /j/i
				:ja
			else
            raise ArugumentError.new("No such a language type: #@language")
			end
		end

		def target=(t)
         @target = t
		end

		def target
         @target
		end

		def image_dir
			"#@target/images"
		end
	end # Parameters

	
	#
	# Interact with a user on console.
	#
	module Ask
		def read
			$stdin.gets.strip
		end

		def print(s)
         $stdout.print s 
		end

		def read_with_default(default)
			r = read.strip
			return r == "" ? default : r
		end
	end
	
	#
	# Ask a user to input parameters.
	#
	class Input
	   include Ask
		
		def initialize(params)
			@params = params
			@original = @params.clone
		end

		def ask
			loop do
				ask_data_path()
				ask_language()
				res = confirm()
				break if /^y$/i =~ res
			end
		end

		def ask_language
			loop do
				default = @params.language
				print "Choose English or Japanese [#{default}/j]: "
				@params.language = read_with_default(default)
				break if /^[ej]$/i =~ @params.language
			end
		end

		def ask_data_path
			default = @params.data_path
			print "Input data_path (default: #{default}): "
			@params.data_path = read_with_default(default)
		end

		def confirm
			print "\ndata_path: %s\nlanguage: %s\n" % [@params.data_path, @params.language]
			res = nil
			loop do
				default = "Y"
				print "Correct? [#{default}/n]: "
				res = read_with_default(default)
				break if /^[yn]$/i =~ res
			end
			if /n/i =~ res
			   @params = @original.clone
			end
			return res
		end
	end # Input

	#
	# Install .htpasswd file with id and password.
	#
	class Installhtpasswd 	  
      include Ask
		
		def initialize(params, htpasswdOfWEBrick)
			@params = params
			@htpasswd = htpasswdOfWEBrick
		end

		def ask
         return if user_exists?(@params.user)
			
			first, second  = "", ""
			loop do
			   print "Input password for #{@params.user} in #{@params.home}/.htpasswd: "
			   first = read
			   print "Input again to confirm: "
			   second = read
				break if first == second
         end
			
			create_entry(@params.user, first)
			return first
		end
		
		def user_exists?(user)
		   return true & @htpasswd.get_passwd("", user, false)
		end

		def create_entry(user, passwd)
			@htpasswd.set_passwd("", user, passwd)
		   @htpasswd.flush
			FileUtils.chmod(0644, @htpasswd.path)
		end
	end # Htpasswd

	
	#
	# Install tDiary files.
	#
   class AbstractSetup
      def initialize(params)
			@params = params
		end
		
		def mkdir(mode, dir)
         if !File.directory?(dir)
			   FileUtils.mkdir_p dir
				FileUtils.chmod(mode, dir)
			end 
		end

		def hasPluginPackage?
         if Debian::Dpkg.status(["tdiary-plugin"]).packages.find_all {|pkg| pkg.installed?}
		      return true
			else
				return false
			end
	   end

		def puts(s)
         $stdout.print "#{s}\n"
		end
	end
	
	#
	# 'Default' mode
	#
	class DefaultSetup < AbstractSetup
		def install
			mkdir(0701, @params.data_path)
			mkdir(0701, @params.target)
			mkdir(0701, @params.image_dir)
         setup_base()
			if hasPluginPackage? 
			   setup_with_plugin_package()
			   install_makerss
			end
		end
		
      def setup_base
         puts "Install tdiary files into #{@params.target}."
			install_files( ["index", "update"] )
		end

		def setup_with_plugin_package
         install_files( ["tb", "squeeze"] )
		end

		def install_files(files)
			files.each do |f|
			   FileUtils.install("#{TDIARY_DIR}/debian-tools/#{f}-debian.rb", "#{@params.target}/#{f}.rb", :mode => 0701)
			end
		end
		
		def install_makerss
         FileUtils::touch(["#{@params.target}/index.rdf"])
			FileUtils::chmod(0701, "#{@params.target}/index.rdf")
		end
	end

	#
	# 'Symlink' mode
	#
	class SymlinkSetup < AbstractSetup
      def install
			mkdir(0777, @params.data_path)
			mkdir(0755, @params.target)
			mkdir(0777, @params.image_dir)
         setup_base()
			if hasPluginPackage?
			   setup_with_plugin_package()
			   install_makerss()
			end
		end
		
      def setup_base
			puts "Make symlinks into #{@params.target}."
         FileUtils.ln_sf "#{TDIARY_DIR}/index.rb",  "#{@params.target}/" 
         FileUtils.ln_sf "#{TDIARY_DIR}/update.rb", "#{@params.target}/" 
		end

		def setup_with_plugin_package
         FileUtils.ln_sf "#{TDIARY_DIR}/misc/plugin/squeeze.rb", "#{@params.target}/"
         FileUtils.ln_sf "#{TDIARY_DIR}/tb.rb",                  "#{@params.target}/"
		end

		def install_makerss
         FileUtils::touch(["#{@params.target}/index.rdf"])
			FileUtils::chmod(0777, "#{@params.target}/index.rdf")
		end
	end

	#
	# 'Copy' mode
	#
	class CopySetup < AbstractSetup
      def install
			mkdir(0777, @params.data_path)
			mkdir(0755, @params.target)
			mkdir(0777, @params.image_dir)
         puts "Copy all flies. You should setup CGI files by yourself."
			FileUtils.cp_r Dir.glob("#{TDIARY_DIR}/*"), "#{@params.target}/"
		end
	end
	
	
	#
	# Install a config file.
	#
   class AbstractInstallConf
      def initialize(params)
		   @params = params
		end

		# Template mothod
		def install
			lines = File.open(file_name()){|f| f.read}
			lines = sub(lines)
			write(lines)
		end

		def file_name
         raise "Not implemented yet."
		end

		def sub(lines)
         raise "Not implemented yet."
		end

		def write(lines)
         raise "Not implemented yet."
		end
	end
	
	#
	# Install /usr/share/tdiary/dot.htaccess to the target directory.
	#
	class Installhtaccess < AbstractInstallConf
		def file_name
         "#{TDIARY_DIR}/dot.htaccess"
		end
		
		def sub(lines)
			lines.gsub!(/^#Options /, "Options ")
			lines.gsub!(/foo/, @params.user)
			return lines
		end

		def write(lines)
			File.open("#{@params.target}/.htaccess", "w") {|f| f.print lines}
		end
	end

	#
	# Install tdiary.conf.sample to the target directory.
	#
	class Installtdiaryconf < AbstractInstallConf
		def write(lines)
			File.open("#{@params.target}/tdiary.conf", "w") {|f| f.print lines}
		end
		
		def file_name
			path = ""
			case @params.language_type
			when :en
				path << TDIARY_DIR + "/misc/i18n/tdiary.conf.sample-en"
			when :ja
				path << TDIARY_DIR + "/tdiary.conf.sample"
			else
            raise "No such language: #{@params.language}"
			end
			return path
		end

		def sub(lines)
         lines.gsub!(%r!a href="doc/!,     'a href="/doc/tdiary/')
			lines.gsub!(/^@data_path =.*$/,   "@data_path = '#{@params.data_path}'")
			lines.gsub!(/^#@cache_path =.*$/, "@cache_path = '/tmp/#{@params.user}_#{unique_id()}'")
			lines.gsub!(/^@options\['sp\.path'\] =.*$/, "")
			lines.gsub!(/^load_cgi_conf.*$/,  "@options['sp.path'] = '#{TDIARY_DIR}/misc/plugin'\nload_cgi_conf\n")
			return lines
		end

		def unique_id
			return Time.now.strftime("%Y%m%d%H%M%S")
		end
	end

   #
	# Check the installed mode
	#
   class CheckMode
      def initialize(params)
         @index = "#{params.target}/index.rb"
		end

      def mode
         if symlink?
				return :symlink
			elsif copy?
				return :copy
			elsif default?
				return :default
			else
            raise "No such a mode. Check #{@index} file."
			end
		end
		
		def symlink?
         return FileTest.symlink?(@index)
		end

		def copy?
         return FileTest.size?(@index) > 200
		end
		
		def default?
         return !copy?
		end
	end
end



#
# MAIN
#

def usage
   out = <<EOF
Usage: #{$0} {default|symlink|copy|update} directory
example) #{$0} default /home/#{ENV['USER']}/public_html/diary

directory is an absolute path where CGI files of tDiary will be put. 

If you set up tDiary for the fist time, choose
  * default if your httpd runs under suEXEC mode, or
  * symlink, or
  * copy for manual installation.

If you update tDiary you used already, choose update.
EOF
   $stderr.print out
end

def first_install(params, setup)
	input = TDiarySetup::Input.new(params)
	input.ask
	webrick  = WEBrick::HTTPAuth::Htpasswd.new("#{params.home}/.htpasswd")
	htpasswd = TDiarySetup::Installhtpasswd.new(params, webrick)
	htpasswd.ask

	setup.install
   htaccess = TDiarySetup::Installhtaccess.new(params)
   htaccess.install
   tdiaryconf = TDiarySetup::Installtdiaryconf.new(params)
	tdiaryconf.install		
end

def update(params)
   check = TDiarySetup::CheckMode.new(params)
	case check.mode
	when :symlink
		TDiarySetup::SymlinkSetup.new(params).install
	when :copy
		TDiarySetup::CopySetup.new(params).install
	when :default
		TDiarySetup::DefaultSetup.new(params).install
	else
      raise "No such a mode: #{check.mode}"
	end
end

def check_args(args)
   if args.length != 2
      usage()
		exit 1
	end	
end

def main(args)
	check_args(args)
	mode, target = args[0], args[1]
	
	params = TDiarySetup::Parameters.new()
	params.target = target
	case mode
	when "default"
		first_install(params, TDiarySetup::DefaultSetup.new(params))
	when "symlink"
		first_install(params, TDiarySetup::SymlinkSetup.new(params))
	when "copy"
		first_install(params, TDiarySetup::CopySetup.new(params))
	when "update"
		update(params)
	else
		usage()
		exit 1
	end	
end

if __FILE__ == $0 
   main(ARGV)
end
